Skip to content
Snippets Groups Projects
Commit a98424dc authored by Mikuláš Dítě's avatar Mikuláš Dítě
Browse files

notes

parent 62e96ee2
No related branches found
No related tags found
No related merge requests found
......@@ -15,7 +15,7 @@ Hlavním přínosem této práce jsou tyto algoritmy:
- na spojování abstraktních syntaktických stromů libovolného jazyka \hyperref[spojovani-ast]{popsaného v kapitole},
- na transformaci AST zpět do kódu pomocí mapování na vstupní zdrojové kódy \hyperref[mapovani-na-kod]{v kapitole}.
 
K nalezení odpovídajících uzlů mezi jednou z spojovaných větví a společnou větví se využívá algoritmu GumTree \cite{Falleri2014}, \hyperref[gumtree]{detailně popsaného v sekci}.
K nalezení odpovídajících uzlů mezi jednou ze spojovaných větví a společnou větví se využívá algoritmu GumTree \cite{Falleri2014}, \hyperref[gumtree]{detailně popsaného v sekci}.
 
Algoritmus \textsc{3dm}, \hyperref[3dm]{popsaný v}, jsem v originální podobně nepoužil. Původní záměr spojit GumTree mapování a Lindholmův algoritmus z \textsc{3dm} na merge byl zmařen tím, jak moc je v \textsc{3dm} mapování a merge propojené. Definice \textit{far move}, mapování složitější než 1:1, a další z GumTree bez zbytečně složitých úprav nelze získat. Proto jsem se \textsc{3dm} pouze inspiroval, ale algoritmus navrhl nový, obecnější a tedy kompatibilní s GumTree.
 
......
% Soudobé merge algoritmy a publikované alternativy {#research}
 
V této kapitole je nejprve popsán společný teoretický základ všech algoritmů. Navazuje shrnutí implementací současných diff algoritmů a jejich využití ve verzovacích nástrojích. Následně je rozebrán základ pro granulárnější porovnávání na úrovni abstraktních syntaktických stromů.
V této kapitole je nejprve popsán společný teoretický základ všech algoritmů. Navazuje shrnutí implementací současných diff algoritmů a jejich využití ve verzovacích nástrojích. Následně je rozebrán základ pro jemnější a tedy přesnější porovnávání na úrovni abstraktních syntaktických stromů.
 
# Teoretický základ pro textové algoritmy
 
......
......@@ -2,6 +2,16 @@
 
Vlastní merge algoritmus popsaný \hyperref[myslenky]{v kapitole} jsem implementoval jako nový modul v aplikaci GumTree. Převedení algoritmu do zdrojového kódu v jazyku Java se obešlo bez pozoruhodných komplikací. Funkční aplikace je na přiloženém médiu, online\footnote{\url{https://github.com/Mikulas/gumtree/releases/}} a může být začleněna do oficiálního repozitáře GumTree.
 
Jako základ celého projektu jsem využil implementaci GumTree \cite{gumtree:src}. Je dostupná pod open-source licencí LGPL-3.0 \cite{gumtree:src:license}. Každá část projektu je implementována jako samostatný modul, což zvyšuje čitelnost, rozšiřitelnost a naopak snižuje nechtěnou propojenost (coupling) napříč nesouvisejícími třídami.
Falleri prozíravě připravil abstrakci `Client`, kterou prozatím implementovali pouze rozdílové příkazy `AnnotatedDiffClient`, `WebDiff` a další. Přidal jsem novou implementaci `MergeClient`. V té se ověří, že uživatel předal argumentem tři soubory a vytvoří instanci třídy `MergeMapping`, což vytvoří mapování uzlů mezi bází a oběma stranami. Projekt GumTree implementuje tři mapování: gumtree (výchozí), change-distiller a xy matcher.
Původní implementace podporuje parsování řady jazyků včetně c, ruby a json. Obsahuje i parser pro PHP, ale pro dávno nepodporovanou verzi 5.2, jejíž vývoj byl oficiálně ukončen v lednu 2011. Navíc je v GumTree využita knihovna pro generování parserů \textsc{antlr} ve staré verzi. Nové gramatiky jsou dostupné pouze pro \textsc{antlr} verze 4 \cite{antlr:homepage}. Část úsilí jsem vložil do hledání vhodné gramatiky pro aktuální php 7.1 a našel jsem open source implementaci od Ivana Kochurkina~\cite{phpgrammar}. Nepodporuje bohužel všechny varianty, co oficiální PHP parser; nefungují například typové nápovědy pro třídy v deklaraci funkce. Jediný vhodnější parser o kterém jsem se dozvěděl je použit v IDE od IntelliJ (jako jsou například IDEA a PhpStorm). Jejich implementace php parseru je ale proprietární a nelze ji pro tuto práci použít.
Analogicky k existujícímu \textsc{antlr} modulu jsem připravil nový samostatný modul \textsc{antlr4}. Díky tomu lze nyní gumtree velmi snadno rozšířit podporu jakéhokoliv jazyka, pro který existuje \textsc{antlr4} předpis. Tyto změny byly zapracovány do pull requestu a odeslány původnímu autorovi ke komentáři a začlenění\footnote{\url{https://github.com/GumTreeDiff/gumtree/pull/47}}.
Zpětně jsem bohužel zjistil, že přestože je tato gramatika udržovaná a aktualizovaná, podporuje pouze funkce do PHP 5.6. Jediné mě známé parsery, která jsou vždy aktuální, jsou tyto dvě: AST zabudované přímo v jádru PHP, které lze zpřístupnit pomocí rozšíření \code{php-ast} \cite{nikic:phpast}. Druhý parser je napsaný přímo v PHP \code{php-parser} \cite{nikic:parser}. Oba projekty spravuje Nikita Popov, jeden z členů PHP internals, kteří se starají o vývoj jazyka. Lepší postup než rozšiřovat GumTree – jak se zpětně ukázalo – by bylo nechat parsování na PHP, přetransformovat tyto struktury do XML, a ty pomocí GumTree mapovat.
## Ukázka výstupu
 
\begin{listing}
......@@ -93,7 +103,7 @@ V tradičním řádkovém algoritmu by tyto změny kolidovaly a vyústily v koli
 
Možná řešení tohoto problému jsou tři: upravit algoritmus mapování GumTree, upravit AST aby současný GumTree fungoval podle očekávání, a nebo zavést složitější systém zámků, který by nalezl související uzly (v této ukázce například \code{\$a} v definici parametrů a ve volání funkce). Částečným řešením je i vyhazování kolize při detekování ztráty změn (zde ztráta změn v uzlu \code{Addition Context}), kterou jsem popisoval u algoritmu \code{createNode} \hyperref[merge-ztrata-zmen]{v sekci}.
 
Rozhodl jsem se upravit AST a uchovávat všechny kontextová uzly. Tím se tento problém vyřešil a merge algoritmus funguje správně. Příklad rozšířeného AST je \hyperref[ukazka-ast]{v příloze}.
Rozhodl jsem se upravit AST a uchovávat všechny kontextové uzly. Tím se tento problém vyřešil a merge algoritmus funguje správně. Příklad rozšířeného AST je \hyperref[ukazka-ast]{v příloze}.
 
\begin{listing}
\begin{minted}{php}
......
......@@ -24,7 +24,7 @@ Architektura infrastruktury:
\centering
\includegraphics[width=1.3\textwidth,scale=0.5]{images/bp-infra}
\end{adjustwidth}
\caption{Vysokoúrovňový pohled na použitou infrastrukturu. Jde o typickou architekturu pro vysokou dostupnost, kde load balancer přeposílá rovnoměrně požadavky mezi více aplikačních serverů, kde výpadek jednoho z nich neznamená nedostupnost. Podobně je redundantní i databáze, která se aktivně používá pouze jedna, ale souběžně běží replika, která je připravena kdykoliv v případě problému nahradit master. Oba aplikační servery i databázové repliky jsou ve fyzicky jiných místech (v terminologii AWS \textit{availability zones}).}
\caption{Vysokoúrovňový pohled na použitou infrastrukturu. Jde o typickou architekturu pro vysokou dostupnost, kde load balancer přeposílá rovnoměrně požadavky mezi více webových (aplikačních) serverů, kde výpadek jednoho z nich neznamená nedostupnost. Podobně je redundantní i databáze, která se aktivně používá pouze jedna, ale souběžně běží replika, která je připravena kdykoliv v případě problému nahradit master. Oba aplikační servery i databázové repliky jsou ve fyzicky jiných místech (v terminologii AWS \textit{availability zones}). Redis slouží jako sdílené úložiště pro sezení (\textit{sessions}).}
\end{figure}
 
\begin{figure}[H]
......
......@@ -4,8 +4,8 @@ authorFN: Dítě
authorWithDegrees: Mikuláš Dítě
author: Mikuláš Dítě
supervisor: Ing. Vojtěch Jirkovský
keywordsCS: rozšíření pro git, slučování stromů, abstraktní syntaktický strom php, řešení kolize slučování, automatizace
keywordsEN: git extension, tree merging, PHP abstract syntax tree, collision resolution, automation
keywordsCS: rozšíření pro git, slučování stromů, abstraktní syntaktický strom php, řešení kolize spojování změn, automatizace
keywordsEN: git extension, tree merging, PHP abstract syntax tree, merge resolution, automation
department: Katedra softwarového inženýrství
placeForDeclarationOfAuthenticity: V~Praze
declarationOfAuthenticityOption: 4
......
# Načtení souborů a parsování do AST
 
Jako základ celého projektu jsem využil implementaci GumTree \cite{gumtree:src}. Je dostupná pod open-source licencí LGPL-3.0 \cite{gumtree:src:license}. Každá část projektu je implementována jako samostatný modul, což zvyšuje čitelnost, rozšiřitelnost a naopak snižuje nechtěnou propojenost (coupling) napříč nesouvisejícími třídami.
Tento algoritmus je částečně popsán \hyperref[gumtree]{v kapitole o GumTree}.
 
Falleri prozíravě připravil abstrakci `Client`, kterou prozatím implementovali pouze rozdílové příkazy `AnnotatedDiffClient`, `WebDiff` a další. Přidal jsem novou implementaci `MergeClient`. V té se ověří, že uživatel předal argumentem tři soubory a vytvoří instanci třídy `MergeMapping`, což vytvoří mapování uzlů mezi bází a oběma stranami. Projekt GumTree implementuje tři mapování: gumtree (výchozí), change-distiller a xy matcher.
Původní implementace podporuje parsování řady jazyků včetně c, ruby a json. Obsahuje i parser pro PHP, ale pro dávno nepodporovanou verzi 5.2, jejíž vývoj byl oficiálně ukončen v lednu 2011. Navíc je v GumTree využita knihovna pro generování parserů \textsc{antlr} ve staré verzi. Nové gramatiky jsou dostupné pouze pro \textsc{antlr} verze 4 \cite{antlr:homepage}. Část úsilí jsem vložil do hledání vhodné gramatiky pro aktuální php 7.1 a našel jsem open source implementaci od Ivana Kochurkina~\cite{phpgrammar}. Nepodporuje bohužel všechny varianty, co oficiální PHP parser; nefungují například typové nápovědy pro třídy v deklaraci funkce. Jediný vhodnější parser o kterém jsem se dozvěděl je použit v IDE od IntelliJ (jako jsou například IDEA a PhpStorm). Jejich implementace php parseru je ale proprietární a nelze ji pro tuto práci použít.
Podobně jako byl implementován \textsc{antlr} jako samostatný modul jsem i \textsc{antlr4} abstrahoval do modulu. Díky tomu lze nyní gumtree velmi snadno rozšířit podporu jakéhokoliv jazyka, pro který existuje \textsc{antlr4} předpis. Tyto změny byly zapracovány do pull requestu a odeslány původnímu autorovi ke komentáři a začlenění\footnote{\url{https://github.com/GumTreeDiff/gumtree/pull/47}}.
Zpětně jsem bohužel zjistil, že přestože je tato gramatika udržovaná a aktualizovaná, podporuje pouze funkce do PHP 5.6. Jediné mě známé parsery, která jsou vždy aktuální, jsou tyto dvě: AST zabudované přímo v jádru PHP, které lze zpřístupnit pomocí rozšíření \code{php-ast} \cite{nikic:phpast}. Druhý parser je napsaný přímo v PHP \code{php-parser} \cite{nikic:parser}. Oba projekty spravuje Nikita Popov, jeden z členů PHP internals, kteří se starají o vývoj jazyka. Lepší postup než rozšiřovat GumTree – jak se zpětně ukázalo – by bylo nechat parsování na PHP, přetransformovat tyto struktury do XML, a ty pomocí GumTree mapovat.
Výsledkem tohoto kroku jsou tři AST. V dalším kroku se budou jejich uzly navzájem porovnávat a mapovat.
Výsledkem tohoto kroku jsou tři AST. V dalším kroku se budou uzly těchto AST navzájem porovnávat a mapovat.
 
Časová komplexita záleží na konkrétním použitém parseru. Pro \textit{Adaptive LL(*)} je $O(n^4)$ (kde $n$~je počet AST uzlů), ale pro typické vstupy je parsování lineární~\cite{Parr2014}.
 
......@@ -64,7 +56,7 @@ když je $left$ nebo $right$ nově vložený uzel, vrací prázdný uzel.
\end{algorithmic}
\end{listing}
 
Vrací přímé potomky $base$, pro které neexistuje vhodné mapování na přímé potomky $side$. Jsou to uzly, které existovali v $base$, ale ne v $side$, a ve výsledném merge by se neměly objevit.
Vrací přímé potomky $base$, pro které neexistuje vhodné mapování na přímé potomky $side$. Jsou to uzly, které existovaly v $base$, ale ne v $side$, a ve výsledném merge by se neměly objevit.
 
\begin{listing}[H]
\begin{algorithmic}
......@@ -193,7 +185,7 @@ Na tomto místě by také bylo vhodné varovat před ztrátou změn. Pokud $righ
\State advance $cur_l$
 
\ElsIf{previous $right$ was locked}
\State append $left$ (including child nodes) to $mergedTree$
\State append $right$ (including child nodes) to $mergedTree$
\State advance $cur_r$
 
\Else
......@@ -211,7 +203,7 @@ Na tomto místě by také bylo vhodné varovat před ztrátou změn. Pokud $righ
\end{algorithmic}
\end{listing}
 
Tato funkce je samotné jádro mého merge algoritmu. Nejprve se sestaví \textit{merge list} pro obě strany. Poté se paralelně prochází potomci obou stran. Pokud mezi aktuálním levým a pravým potomkem existuje mapování, rekurzivně se spočte jejich spojení, jinak se využije systému zámků. Jsou-li oba předchozí uzly zamknuté, současné uzly se musí shodovat, jinak nastává kolize (jedna ze semknutých ntic by se musela rozdělit). Pokud je zamknutý pouze jeden uzel, jedná se o přemístění nebo vložení, které není v druhé větvi. Poslední možnost nastává, pokud se uzly implicitně shodují, ale v okolí nedošlo k žádné změně, která by vytvořila zámky. Pokud by se uzly neshodovali, jedna ze stran by byla označena jako odstranění a druhá jako vkládání a došlo by k vytvoření zámků.
Tato funkce je samotné jádro mého merge algoritmu. Nejprve se sestaví \textit{merge list} pro obě strany. Poté se paralelně prochází potomci obou stran. Pokud mezi aktuálním levým a pravým potomkem existuje mapování, rekurzivně se spočte jejich spojení, jinak se využije systému zámků. Jsou-li oba předchozí uzly zamknuté, současné uzly se musí shodovat, jinak nastává kolize (jedna ze semknutých ntic by se musela rozdělit). Pokud je zamknutý pouze jeden uzel, jedná se o přemístění nebo vložení, které není v druhé větvi. Poslední možnost nastává, pokud se uzly implicitně shodují, ale v okolí nedošlo k žádné změně, která by vytvořila zámky. Pokud by se uzly neshodovaly, jedna ze stran by byla označena jako odstranění a druhá jako vkládání a došlo by k vytvoření zámků.
 
Pokud je pouze jeden z kurzorů $cur_l$ nebo $cur_r$ je nevalidní, došlo k vkládání nového uzlu na konec. Všechny funkce s tím musí počítat a mít stejný efekt, jako kdyby místo nevalidního kurzoru dostaly prázdný strom.
 
......@@ -233,7 +225,7 @@ První z nich je pomocí nějakých pravidel každý uzel transformovat do kódu
 
Druhou možností je namapování výstupních AST uzlů na původní zdrojové kódy. Dílčí tokeny budou naformátované vždy tak, jak je napsal uživatel. U příkladu výše by ve výsledném souboru bylo opravdu `1e6`, pokud to tak uživatel zadal na vstupu. Menší problém tvoří bílé znaky, které jsou pro čitelnost kódu velmi důležité (díky odřádkování se typicky seskupují související příkazy a podobně). Parser typicky bílé znaky zahazuje a při spojování vstupních AST s nimi tedy nemůžeme pracovat.
 
Algoritmus pro namapování AST na vstupní zdrojové kódy následovně. Platí pro PHP AST generované zvoleným parserem, které má všechny tisknutelné uzly v listech.
Následující algoritmus mapuje AST na vstupní zdrojové kódy. Očekává AST, které má všechny tisknutelné uzly v listech.
 
\begin{listing}[H]
\begin{algorithmic}
......
### 3DM
\label{3dm}
 
\textsc{3dm} \cite{Lindholm2001} je zkratkou pro \textit{3-way merging, Differencing and Matching}. Jde o nejstarší mě známou práci, který se třícestnému merge věnuje. Algoritmy ChangeDistilling a GumTree popisují pouze diff a generování editačního skriptu.
\textsc{3dm} \cite{Lindholm2001} je zkratkou pro \textit{3-way merging, Differencing and Matching}. Jde o nejstarší mně známou práci, která se třícestnému merge věnuje. Algoritmy ChangeDistilling a GumTree popisují pouze diff a generování editačního skriptu.
 
Přestože práce primárně popisuje spojování XML dokumentů, lze většinu konceptů a algoritmů použít i na AST.
 
......
......@@ -3,4 +3,4 @@
 
Jde o první článek \cite{Fluri2007}, ve kterém se přístup FastMatch aplikuje na zdrojový kód. Přínosy jsou dva: vytvoření testovací sady, a téměř 45 % zrychlení oproti FastMatch.
 
Pro adaptaci na AST se odstraňuje předpoklad, že v pravém stromu je maximálně jeden list, který se může namapovat na korespondující list v levém stromu~\cite[kapitola 2.2.2]{Fluri2007}.
Pro adaptaci na AST se odstraňuje předpoklad, že v pravém stromu je maximálně jeden list, který se může namapovat na odpovídající list v levém stromu~\cite[kapitola 2.2.2]{Fluri2007}.
......@@ -111,10 +111,10 @@ Jde o rozšiřující implementaci Patience diff, původně v Jgit~\cite{jgit:hi
 
Výstup je často stejný jako u Patience diff. Rozdíl může nastat pouze pokud se v kódu vyskytuje více neunikátních řádků. Hlavním rozdílem oproti Patience je lepší výkon. Podle stejného měření jako výše běžel Histogram diff 1.90~(1.74+0.15) sekund, tedy dokonce o něco kratší dobu, než Myers diff.
 
### Heurestiky
### Heuristiky
 
Kromě samotných diff algoritmů ovlivňují přehlednost výstupu tzv.~\textit{sliders}~(posuvníky). Jde o situace, kde kraje patche lze cyklicky posouvat, například u složených závorek mezi těly metod. Situace je přehledně viditelná na~\hyperref[slider-example]{ukázce}. \newline
Git od verze~2.11 implementuje postprocessing heurestiku, která správně opraví~97,8~\% těchto situací~\cite{git:slider}.
Git od verze~2.11 implementuje postprocessing heuristiku, která správně opraví~97,8~\% těchto situací~\cite{git:slider}.
 
\begin{listing}
\centering
......
......@@ -9,7 +9,7 @@ Formálně je problém Hirschbergem definován takto~\cite{Hirschberg1977}: řet
 
\newcommand*{\Ss}{\ensuremath{\mathrm{S}_s}}
 
Funkce $\Ss(C)$ je definována jako zobrazení řetězců do množiny řetězců, kde pro parametr $C$ funkce $\Ss$ vrací množinu všech podřetězců $C$.\footnote{První výskyt množinové definice jsem nalezl v Maier~1978~\cite{Maier1978} a dále v Hsu a Du 1982~\cite{Hsu1984}. Hirschberg v své práci~\cite{Hirschberg1977}, která navazuje na jeho práci disertační~\cite{Hirschberg1975}, tuto definici nepoužívá.}
Funkce $\Ss(C)$ je definována jako zobrazení řetězců do množiny řetězců, kde pro parametr $C$ funkce $\Ss$ vrací množinu všech podřetězců $C$.\footnote{První výskyt množinové definice jsem nalezl v Maier~1978~\cite{Maier1978} a dále v Hsu a Du 1982~\cite{Hsu1984}. Hirschberg v své práci~\cite{Hirschberg1977} tuto definici nepoužívá.}
 
Společná podsekvence $C$ řetězců $a$, $b$ je právě taková podsekvence, která je zároveň podsekvence $a$ a podsekvence $b$. Formálně $\Ss(a, b) = \Ss(a) \cap \Ss(b)$. Definuji obecnou množinovou variantu $\Ss(A) = \bigcap\limits_i \Ss(A_i)$, kde $A$ je množina řetězců.
 
......@@ -33,7 +33,7 @@ Existuje pro dané $k \in \mathbb{N}$ a množinu řetězců $R$ aspoň jedno $c$
0 & \mathrm{jinak}
\end{cases}
\end{align*}
kde $A$, $B$ jsou řetězce nad stejnou abecedou a $k \in \mathbb{N}$ je požadovaná minimální délka podsekvence.
kde $R$ je množina řetězců nad stejnou abecedou a $k \in \mathbb{N}$ je požadovaná minimální délka podsekvence.
 
Obecné řešení YNLCS problému -- pro $R$ s velikostí větší než 2 -- má třídu složitosti NP-úplné \cite{Maier1978}. Pro tuto práci to znamená, že nelze efektivně přímo využít LCS pro tři velké vstupy (větev 1, společný základ, větev 2). Toto řeší algoritmus \code{diff3} (\hyperref[diff3]{kapitola}).
 
......@@ -49,7 +49,7 @@ TODO what, není to blbost?
 
### Naivní řešení
 
\begin{listing}
\begin{listing}[H]
\begin{algorithmic}
\State $A_i :=$ $i$. řádek prvního řetězce $\mathcal{A}$
\State $B_j :=$ $j$. řádek druhého řetězce $\mathcal{B}$
......
......@@ -413,11 +413,9 @@ hang
Overview
fallback
Team
heurestiku
Kochurkina
target
bno
Heurestiky
load
anchor
Paterson
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment