doku 2
This commit is contained in:
parent
6678a45c7d
commit
5235189966
7
.gitignore
vendored
7
.gitignore
vendored
@ -7,3 +7,10 @@ build/
|
||||
*.svg
|
||||
*.ps
|
||||
*.pdf
|
||||
*.aux
|
||||
*.fdb_latexmk
|
||||
*.fls
|
||||
*.log
|
||||
*.out
|
||||
*.synctex.gz
|
||||
*.toc
|
||||
|
||||
@ -25,13 +25,34 @@
|
||||
\usepackage{graphicx}
|
||||
\graphicspath{{Images/}} % Při vkládání obrázků se bude prefixovat tato relativní cesta.
|
||||
|
||||
% \usepackage{inconsolata}
|
||||
% \usepackage{zi4}
|
||||
% \usepackage{courier}
|
||||
|
||||
|
||||
% Při použití tohoto balíku začnou fungovat odkazy v textu.
|
||||
% Zkuste třeba kliknout na odkazy v textu (např. "1.1" na straně 2) nebo v seznamu obrázků/tabulek.
|
||||
% `hidelinks` skryje ošklivé výchozí rámečky kolem odkazů.
|
||||
\usepackage[hidelinks]{hyperref}
|
||||
|
||||
\usepackage{listings}
|
||||
% \usepackage{minted}
|
||||
\usepackage{xcolor}
|
||||
|
||||
% Define code snippet settings
|
||||
\lstset{
|
||||
language=C, % Set the programming language for the code snippet
|
||||
basicstyle=\ttfamily, % Set the font for the code
|
||||
keywordstyle=\color{blue}, % Set color for keywords
|
||||
commentstyle=\color{green!60!black}, % Set color for comments
|
||||
stringstyle=\color{red}, % Set color for strings
|
||||
% numbers=left, % Show line numbers
|
||||
% numberstyle=\tiny\color{gray}, % Style for line numbers
|
||||
breaklines=true, % Enable line breaks
|
||||
% frame=single,
|
||||
showstringspaces=false % Don't show spaces in strings
|
||||
}
|
||||
|
||||
|
||||
% Začátek dokumentu
|
||||
\begin{document}
|
||||
|
||||
@ -81,8 +102,8 @@ zvoleném definičním oboru.
|
||||
Celé zadání je k~dispozici na \url{https://www.kiv.zcu.cz/studies/predmety/pc/data/works/sw2024-02.pdf}.
|
||||
|
||||
\section{Analýza úlohy}
|
||||
Úloha se skládá ze dvou hlavních problémů: matematickou funkci je nejprve třeba analyzovat a~poté
|
||||
použít výstup této analýzy ke zjištění jejích hodnot v~jednotlivých bodech a~vykreslení grafu.
|
||||
Práce se zabývá dvěma hlavními úlohami: matematickou funkci je nejprve třeba analyzovat a~poté
|
||||
použít výstup této analýzy k její evaluaci a~vykreslení grafu.
|
||||
|
||||
\subsection{Analýza funkce}
|
||||
Analýzu zadané matematické funkce je vhodné rozdělit do dvou částí --- analýzu lexikální a~syntaktickou.
|
||||
@ -99,9 +120,10 @@ Typy tokenů využité v~této práci jsou uvedeny v~tabulce~\ref{tab:tokens}.
|
||||
|
||||
\begin{table}[]
|
||||
\centering
|
||||
\caption{Použité typy tokenů}\label{tab:tokens}
|
||||
\begin{tabular}{|l|l|}
|
||||
\hline
|
||||
označení & popis \\ \hline
|
||||
\textbf{označení} & \textbf{popis} \\ \hline
|
||||
\texttt{NUMBER} & konstanta \\ \hline
|
||||
\texttt{PLUS} & operátor sčítání \texttt{+} \\ \hline
|
||||
\texttt{MINUS} & operátor odečítání nebo negace \texttt{-} \\ \hline
|
||||
@ -116,21 +138,19 @@ Typy tokenů využité v~této práci jsou uvedeny v~tabulce~\ref{tab:tokens}.
|
||||
\texttt{EOF} & konec vstupu \\ \hline
|
||||
\texttt{ERROR} & chyba (nerozpoznatelná sekvence) \\ \hline
|
||||
\end{tabular}
|
||||
\caption{Použité typy tokenů}
|
||||
\label{tab:tokens}
|
||||
\end{table}
|
||||
|
||||
\subsubsection{Syntaktická analýza}
|
||||
Následuje analýza syntaktická, během které je zadaná funkce zpracována do stromové struktury, kterou je pak možné
|
||||
využít pro vyhodnocování funkce v~jednotlivých bodech.
|
||||
Následuje analýza syntaktická, během které je zadaná funkce zpracována do stromové struktury.
|
||||
Existuje mnoho způsobů, jak tuto analýzu provést, mezi základní patří např.~rekurzivní sestup nebo algoritmus shunting-yard.
|
||||
V~této práci je použita metoda rekurzivního sestupu, která je relativně jednoduchá a~přehledná --- program je možné
|
||||
mechanicky vytvořit z~gramatiky zpracovávaného jazyka. Mezi její další výhody patří, že se oproti
|
||||
algoritmu shunting-yard dokáže lépe vypořádat s~unárními operátory.
|
||||
|
||||
Analyzátor rekurzivním sestupem lze obecně vytvořit z~gramatiky popisující zpracovávaný jazyk
|
||||
pomocí sady funkcí, které odpovídají jednotlivým pravidlům gramatiky. To je v~tomto případě
|
||||
snadné, protože pro zpracování matematického výrazu lze sestavit gramatiku
|
||||
pomocí sady funkcí, které odpovídají jednotlivým pravidlům této gramatiky.
|
||||
|
||||
Pro zpracování matematických lze sestavit gramatiku
|
||||
|
||||
\begin{verbatim}
|
||||
<expression> = <term> { ( PLUS | MINUS ) <term> }
|
||||
@ -149,4 +169,142 @@ kde \verb|{}| značí iteraci, \verb|[]| volitelnost a~\texttt{|} jednu z~možno
|
||||
Výrazy v~\verb|<>| jsou neterminály, zatímco ostatní symboly jsou terminály odpovídající
|
||||
tokenům z~lexikální analýzy.
|
||||
|
||||
Tato gramatika je typu LL(1), což znamená, že je možné se při zpracování
|
||||
vždy rozhoudnout pouze na základě jednoho symbolu ze~vstupu, což zjednodušuje
|
||||
implementaci analyzátoru.
|
||||
|
||||
V~případě, že je zadaná funkce syntakticky správná, je během této analýzy možné
|
||||
vytvořit stromovou strukturu, kde bude každý uzel
|
||||
reprezentovat jednu hodnotu nebo matematickou operaci a~jeho potomci budou odpovídat
|
||||
jejím operandům.
|
||||
Tuto strukturu lze pak využít pro vyhodnocování zadané funkce v~jednotlivých bodech,
|
||||
což je klíčové pro vykreslení grafu. Příklad stromové struktury pro funkci $\sin(2x)+ 1$
|
||||
je zobrazen na obrázku~\ref{fig:graph}.
|
||||
|
||||
\begin{figure}[]
|
||||
\centering
|
||||
\includegraphics[width=0.6\textwidth]{graph}
|
||||
\caption{Strom výrazu $\sin(2x) + 1$}\label{fig:graph}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Vykreslení grafu}
|
||||
Graf je možné vykreslit pomocí lomené čáry. To lze v~jazyce PostScript realizovat vytvořením
|
||||
cesty pomocí příkazů \texttt{moveto} a~\texttt{lineto} a~jejím vykreslením příkazem \texttt{stroke}.
|
||||
|
||||
Body, které budou tvořit lomenou čáru, budou zřejmě ve tvaru $(x, f(x))$, kde $f(x)$ je hodnota
|
||||
zadané funkce v~bodě $x$. Zbývá otázka, jaké hodnoty vybrat pro $x$. Jednou z možností je zvolit
|
||||
pevně danou šířku kroku, např.~$d = 0.1$, a vyhodnocovat funkci v~bodech $x = x_d + kd$,
|
||||
kde $k$ postupně nabývá hodnot $0, 1, 2, \ldots$, dokud $x_d + kd \leq x_h$, a kde $x_d$ je dolní
|
||||
a~$x_h$ horní mez zadaného intervalu pro vykreslení grafu. Nevýhodou tohoto přístupu je, že
|
||||
nebere v~potaz délku zadaného intervalu, což může vést k~příliš malému počtu bodů v~případě malého intervalu a~naopak.
|
||||
Lepší variantou je tedy namísto pevného kroku $d$ zvolit pevný počet bodů~$n$,
|
||||
které budou tvořit lomenou čáru. V~takovém případě je krok možné určit jako $d = (x_h - x_d) / n$.
|
||||
|
||||
V implementaci je také třeba vyřešit body, kde funkce není definována.
|
||||
|
||||
\section{Popis implementace}
|
||||
Program je rozdělen do několika modulů. Každý z~nich má vlastní rozhraní, a~implementace
|
||||
každého modulu je nezávislá na implementaci ostatních modulů.
|
||||
|
||||
Každý modul je popsán v~následujících podsekcích včetně popisu jeho rozhraní.
|
||||
|
||||
\subsection{Vstupní bod programu --- \texttt{main.c}}
|
||||
|
||||
Výkon programu začíná ve~funkci \texttt{main}, která má na starosti následující úkoly:
|
||||
|
||||
\begin{itemize}
|
||||
\item Ověření správného počtu vstupních argumentů. V~případě, že je počet argumentů
|
||||
neplatný, program vypíše návod k~použití pomocí funkce \texttt{print\_usage} a~ukončí se.
|
||||
\item Načtení rozsahu pro vykreslení grafu pomocí funkce \texttt{parse\_range}. Pokud
|
||||
tento argument není uveden, použije se výchozí rozsah $x_d = y_d = -10$ a~$x_h = y_h = 10$.
|
||||
\item Zpracování zadané funkce pomocí modulu \texttt{parser}, který vytvoří strom
|
||||
reprezentující tuto funkci.
|
||||
\item Vykreslení grafu funkce na zadaném rozsahu pomocí modulu \texttt{ps\_graph}
|
||||
do zadaného souboru. K~tomuto účelu je zavolána funkce \texttt{export\_to\_file}.
|
||||
\item \textit{Pokud je program zkompilován s~definicí makra \texttt{ENABLE\_GRAPHVIZ\_EXPORT}, je
|
||||
strom výrazu vyexportován do souboru ve formátu \texttt{dot}.}
|
||||
\item Uvolnění alokovaných zdrojů a vrácení návratového kódu.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Chyby --- modul \texttt{error\_buffer}}
|
||||
|
||||
\subsection{Lexikální analyzátor --- modul \texttt{lexer}}
|
||||
Lexikální analyzátor se stará o~tokenizaci vstupního řetězce.
|
||||
|
||||
\subsubsection{Výčet \texttt{token\_type}}
|
||||
Typy tokenů jsou reprezentovány výčtem \texttt{token\_type}, který obsahuje
|
||||
všechny typy tokenů uvedené v~tabulce~\ref{tab:tokens}.
|
||||
Názvy typů odpovídají označením v~tabulce s~prefixem \texttt{TOK\_}.
|
||||
|
||||
\subsubsection{Struktura \texttt{token}}
|
||||
Token je reprezentován strukturou \texttt{token}, která obsahuje typ tokenu a~případně
|
||||
jeho hodnotu, pokud je to vzhledem k~jeho typu relevantní.
|
||||
|
||||
\subsubsection{Struktura \texttt{lexer}}
|
||||
Struktura \texttt{lexer} obsahuje stav lexikálního analyzátoru, tj.~ukazatel na zpracovávaný
|
||||
řetězec, ukazatel na aktuální pozici v~tomto řetězci, aktuální token a~další informace potřebné pro zpracování.
|
||||
|
||||
\subsubsection{Inicializace}
|
||||
Funkce
|
||||
\begin{lstlisting}
|
||||
void lex_init(struct lexer *lex, const char *str, const char *variable_name);
|
||||
\end{lstlisting}
|
||||
|
||||
% \texttt{void lex\_init(struct lexer *lex, const char *str, const char *variable\_name);}
|
||||
inicializuje lexikální analyzátor \texttt{lex} pro zpracování řetězce \texttt{str}.
|
||||
\texttt{variable\_name} je název proměnné, která se ve vstupním řetězci může vyskytnout.
|
||||
|
||||
Deinicializace není třeba, protože při inicializaci ani činnosti tohoto modulu není prováděna žádná alokace paměti.
|
||||
|
||||
\subsubsection{Získávání tokenů}
|
||||
Aktuální token je možné získat pomocí funkce
|
||||
\begin{lstlisting}
|
||||
struct token *lex_token(struct lexer *lex);
|
||||
\end{lstlisting}
|
||||
která na něj vrací ukazatel.
|
||||
Pro získání dalšího tokenu je třeba zavolat funkci
|
||||
\begin{lstlisting}
|
||||
void lex_next(struct lexer *lex);
|
||||
\end{lstlisting}
|
||||
|
||||
\subsubsection{Chyba při lexikální analýze}
|
||||
Pokud nastane během lexikální analýzy chyba, tzn.~v~řetězci se vyskytne sekvence znaků,
|
||||
kterou nelze převést na token, je vytvořen token typu \texttt{TOK\_ERROR}.
|
||||
V~tomto případě je možné pomocí funkce
|
||||
\begin{lstlisting}
|
||||
enum error_code lex_get_error(
|
||||
const struct lexer *lex);
|
||||
\end{lstlisting}
|
||||
získat chybový kód.
|
||||
Pro získání textové reprezentace chyby lze využít funkci
|
||||
\begin{lstlisting}
|
||||
const char *lex_get_error_text(
|
||||
const struct lexer *lex);
|
||||
\end{lstlisting}
|
||||
|
||||
\subsubsection{Diagnostika}
|
||||
Pro diagnostické účely je možné získat textovou reprezentaci typu tokenu funkcí
|
||||
\begin{lstlisting}
|
||||
const char *lex_token_str(enum token_type token);
|
||||
\end{lstlisting}
|
||||
|
||||
V~případě, že nastane chyba při dalším zpracování, je možné přehledně vypsat
|
||||
aktuální pozici v~řetězci pomocí funkce
|
||||
\begin{lstlisting}
|
||||
void lex_print_position(const struct lexer *lex, struct error_buffer *eb);
|
||||
\end{lstlisting}
|
||||
která ji vypíše do zásobníku \texttt{eb}.
|
||||
|
||||
\subsection{Syntaktický analyzátor --- modul \texttt{parser}}
|
||||
|
||||
|
||||
|
||||
\subsection{Strom výrazu --- modul \texttt{tree}}
|
||||
|
||||
\subsection{Vykreslení grafu --- modul \texttt{ps\_graph}}
|
||||
|
||||
\section{Uživatelská příručka}
|
||||
|
||||
\section{Závěr}
|
||||
|
||||
\end{document}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user