Compare commits

...

5 Commits

Author SHA1 Message Date
tovjemam
d88234f394 komentare navic 2025-01-05 17:48:34 +01:00
tovjemam
b713991e4b doku 4 2025-01-04 22:25:25 +01:00
tovjemam
ddec1650e6 mf 2 2025-01-04 22:25:16 +01:00
tovjemam
51a1db8539 odevdz nekomitovat 2025-01-04 22:25:05 +01:00
tovjemam
82e865e0d4 kodovani (splint mrdka) 2025-01-04 22:24:52 +01:00
16 changed files with 287 additions and 160 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.vs/ .vs/
.vscode/ .vscode/
build/ build/
odevdz/
*.cppcheck *.cppcheck
*.dot *.dot
*.png *.png

View File

@ -1,12 +1,12 @@
CC = gcc CC = gcc
CFLAGS = -Wall -pedantic CFLAGS = -Wall -pedantic
LDFLAGS = -lm LDFLAGS = -lm
BIN = graph.exe BIN = graph.exe
OBJ = errors.o lex.o main.o math_functions.o parser.o ps_graph.o tree.o OBJ = errors.o lex.o main.o math_functions.o parser.o ps_graph.o tree.o
%.o: %.c %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@ $(CC) -c $(CFLAGS) $< -o $@
$(BIN): $(OBJ) $(BIN): $(OBJ)
$(CC) $^ -o $@ $(LDFLAGS) $(CC) $^ -o $@ $(LDFLAGS)

View File

@ -1,9 +1,9 @@
BIN = graph.exe BIN = graph.exe
OBJ = errors.o lex.o main.o math_functions.o parser.o ps_graph.o tree.o OBJ = errors.obj lex.obj main.obj math_functions.obj parser.obj ps_graph.obj tree.obj
.c.obj: .c.obj:
cl $< /c cl $< /c
$(BIN): $(OBJ) $(BIN): $(OBJ)
cl $(OBJ) /Fe$(BIN) cl $(OBJ) /Fe$(BIN)

View File

@ -145,13 +145,13 @@ Typy tokenů využité v~této práci jsou uvedeny v~tabulce~\ref{tab:tokens}.
\label{sec:ana} \label{sec:ana}
Následuje analýza syntaktická, během které je zadaná funkce zpracována do stromové struktury. 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. 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é V~této práci je použita metoda rekurzivního sestupu, která je relativně jednoduchá a~přehledná.
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 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 této gramatiky. pomocí sady funkcí, které odpovídají jednotlivým pravidlům této gramatiky.
\newpage
Pro zpracování matematických lze sestavit gramatiku Pro zpracování matematických lze sestavit gramatiku
\begin{verbatim} \begin{verbatim}
@ -171,9 +171,9 @@ 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í Výrazy v~\verb|<>| jsou neterminály, zatímco ostatní symboly jsou terminály odpovídající
tokenům z~lexikální analýzy. tokenům z~lexikální analýzy.
Tato gramatika je typu LL(1), což znamená, že je možné se při zpracování % 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 % vždy rozhoudnout pouze na základě jednoho symbolu ze~vstupu, což zjednodušuje
implementaci analyzátoru. % implementaci analyzátoru.
V~případě, že je zadaná funkce syntakticky správná, je během této analýzy možné 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 vytvořit stromovou strukturu, kde bude každý uzel
@ -210,6 +210,8 @@ každého modulu je (až na výjimky) nezávislá na implementaci ostatních mod
Každý modul včetně jeho rozhraní je popsán v~následujících podsekcích. Každý modul včetně jeho rozhraní je popsán v~následujících podsekcích.
\newpage
\subsection{Vstupní bod programu --- \texttt{main.c}} \subsection{Vstupní bod programu --- \texttt{main.c}}
Výkon programu začíná ve~funkci \texttt{main}, která má na starosti následující úkoly: Výkon programu začíná ve~funkci \texttt{main}, která má na starosti následující úkoly:
@ -299,11 +301,29 @@ toho její číselnou hodnotu.
\subsubsection{Vytvoření uzlu} \subsubsection{Vytvoření uzlu}
Pro vytvoření uzlu je možné použít funkce, jejichž názvy začínají \texttt{node\_create\_}: Pro vytvoření uzlu je možné použít funkce, jejichž názvy začínají \texttt{node\_create\_}:
\texttt{node\_create\_const} (konstanta), \texttt{node\_create\_x} (proměnná), \texttt{node\_create\_add} (sčítání),
\texttt{node\_create\_sub} (odečítání), \texttt{node\_create\_mult} (násobení), \texttt{node\_create\_div} (dělení),
\texttt{node\_create\_pow} (umocnění), \texttt{node\_create\_neg} (negace), \texttt{node\_create\_fn} (funkce).
Většina těchto funkcí přijímá jako argumenty ukazatele na uzly, které se mají stát operandy vytvářeného uzlu. \begin{lstlisting}
struct expr_node *node_create_const(double val);
struct expr_node *node_create_x(void);
struct expr_node *node_create_add(
struct expr_node *left, struct expr_node *right);
struct expr_node *node_create_sub(
struct expr_node *left, struct expr_node *right);
struct expr_node *node_create_mult(
struct expr_node *left, struct expr_node *right);
struct expr_node *node_create_div(
struct expr_node *left, struct expr_node *right);
struct expr_node *node_create_pow(
struct expr_node *base, struct expr_node *power);
struct expr_node *node_create_neg(
struct expr_node *unop);
struct expr_node *node_create_fn(
size_t fn_idx, struct expr_node **args);
\end{lstlisting}
Tyto funkce vrací dynamicky alokovaný uzel, který je třeba po použití uvolnit (v~případě, že se uzel stane potomkem jiného uzlu, je
automaticky uvolněn s~tímto uzlem).
\subsubsection{Evaluace uzlu} \subsubsection{Evaluace uzlu}
Uzel lze vyhodnotit v~daném bodě pomocí funkce Uzel lze vyhodnotit v~daném bodě pomocí funkce
@ -312,8 +332,8 @@ Uzel lze vyhodnotit v~daném bodě pomocí funkce
const struct expr_node *node, const struct expr_node *node,
double x, double *y); double x, double *y);
\end{lstlisting} \end{lstlisting}
která v~případě úspěchu vrací \texttt{EVAL\_OK} a~do \texttt{y} uloží funkční hodnotu. Pokud která v~případě úspěchu vrací \texttt{EVAL\_OK} a~do \texttt{y} uloží výslednou hodnotu. Pokud
funkce není v tomto bodě definována, je výsledkem \texttt{EVAL\_ERROR}. funkce není v~tomto bodě definována, je výsledkem \texttt{EVAL\_ERROR}.
\subsubsection{Uvolnění uzlu} \subsubsection{Uvolnění uzlu}
Uzel je možné uvolnit pomocí funkce Uzel je možné uvolnit pomocí funkce
@ -409,6 +429,8 @@ inicializuje lexikální analyzátor \texttt{lex} pro zpracování řetězce \te
Deinicializace není třeba, protože při inicializaci ani činnosti tohoto modulu není prováděna žádná alokace paměti. Deinicializace není třeba, protože při inicializaci ani činnosti tohoto modulu není prováděna žádná alokace paměti.
\newpage
\subsubsection{Získávání tokenů} \subsubsection{Získávání tokenů}
Aktuální token je možné získat pomocí funkce Aktuální token je možné získat pomocí funkce
\begin{lstlisting} \begin{lstlisting}
@ -416,7 +438,7 @@ Aktuální token je možné získat pomocí funkce
const struct lexer *lex); const struct lexer *lex);
\end{lstlisting} \end{lstlisting}
která na něj vrací ukazatel. která na něj vrací ukazatel.
Pro získání dalšího tokenu je třeba zavolat funkci Pro načtení dalšího tokenu je třeba zavolat funkci
\begin{lstlisting} \begin{lstlisting}
void lex_next(struct lexer *lex); void lex_next(struct lexer *lex);
\end{lstlisting} \end{lstlisting}
@ -451,7 +473,7 @@ která ji vypíše do zásobníku \texttt{eb}.
\subsection{Syntaktický analyzátor --- modul \texttt{parser}} \subsection{Syntaktický analyzátor --- modul \texttt{parser}}
Modul \texttt{parser} využívá tokeny získané lexikálním analyzátorem k~syntaktické Modul \texttt{parser} využívá tokeny získané lexikálním analyzátorem k~syntaktické
analýze a~vytváří stromu zadaného výrazu. analýze a~vytváří strom zadaného výrazu.
\subsubsection{Postup analýzy} \subsubsection{Postup analýzy}
Syntantický analyzátor je implementován pomocí rekurzivního sestupu. Syntantický analyzátor je implementován pomocí rekurzivního sestupu.
@ -467,7 +489,7 @@ se skládá z~prefixu \texttt{parse\_} a~názvu neterminálu:
Pokud je tedy výrazem například sečtení tří podvýrazů, jsou vytvořeny dva uzly sčítání: operandy prvního z nich jsou dva tyto podvýrazy a operandy druhého Pokud je tedy výrazem například sečtení tří podvýrazů, jsou vytvořeny dva uzly sčítání: operandy prvního z nich jsou dva tyto podvýrazy a operandy druhého
jsou první uzel a~třetí podvýraz. Poslední uzel je návratovou hodnotou. jsou první uzel a~třetí podvýraz. Poslední uzel je návratovou hodnotou.
\item Funkce \texttt{parse\_term} postupuje obdobně jako \texttt{parse\_expression}, ale pro násobení a~dělení. Volá \texttt{parse\_unary}. \item Funkce \texttt{parse\_term} postupuje obdobně jako \texttt{parse\_expression}, ale pro násobení a~dělení. Volá \texttt{parse\_unary}.
\item \texttt{parse\_unary} zjistí, zda je dalším tokenem unární operátor. V~případě, že se jedná o~mínus vytvoří uzel pro negaci, \item \texttt{parse\_unary} zjistí, zda je dalším tokenem unární operátor. V~případě, že se jedná o~mínus, vytvoří uzel pro negaci,
jehož operandem se stane návratová hodnota \texttt{parse\_power}, jinak je tato funkce zavolána přímo. jehož operandem se stane návratová hodnota \texttt{parse\_power}, jinak je tato funkce zavolána přímo.
\item \texttt{parse\_power} nejprve zavolá \texttt{parse\_factor} a~poté zjistí, zda následuje operátor umocnění. V~případě, že ano, vytvoří uzel pro umocnění, \item \texttt{parse\_power} nejprve zavolá \texttt{parse\_factor} a~poté zjistí, zda následuje operátor umocnění. V~případě, že ano, vytvoří uzel pro umocnění,
jehož levým operandem je návratová hodnota \texttt{parse\_factor} a~pro zpracování pravého operandu zavolá znovu \texttt{parse\_unary}, jelikož jehož levým operandem je návratová hodnota \texttt{parse\_factor} a~pro zpracování pravého operandu zavolá znovu \texttt{parse\_unary}, jelikož
@ -558,7 +580,7 @@ Tato funkce vypíše do souboru \texttt{file} příkazy jazyka PostScript:
\subsection{Použití v~\texttt{KIV/UPG}}\label{sec:upg} \subsection{Použití v~\texttt{KIV/UPG}}\label{sec:upg}
Moduly umožňující vyhodnocení funkcí byly využity i v~rámci semestrální práce z předmětu \texttt{KIV/UPG}, Moduly umožňující vyhodnocení funkcí byly využity i v~rámci semestrální práce z předmětu \texttt{KIV/UPG},
kde sloužily k~animaci elektrických nábojů v~čase. kde sloužily k~animaci elektrických nábojů v~čase.
Z tohoto důvodu se v~práci nachází některé nadbytečné funkce: Z tohoto důvodu se v~práci nachází některé \uv{nadbytečné} funkce:
\begin{itemize} \begin{itemize}
\item Vypisování chyb do zásobníku \texttt{error\_buffer} namísto standardního výstupu, jelikož \item Vypisování chyb do zásobníku \texttt{error\_buffer} namísto standardního výstupu, jelikož
@ -580,13 +602,13 @@ Tímto vznikne spustitelný soubor \texttt{graph.exe}.
\subsection{Použití} \subsection{Použití}
Program se spouští příkazem \texttt{graph.exe} s~následujícími argumenty: Program se spouští příkazem \texttt{graph.exe} s~následujícími argumenty:
\begin{itemize} \begin{enumerate}
\item Předpis funkce, která má být vykreslena. \item Předpis funkce, která má být vykreslena.
\item Název souboru, do kterého se má graf uložit. \item Název souboru, do kterého se má graf uložit.
\item Volitelně: Rozsah grafu ve tvaru $x_d\texttt{:}x_h\texttt{:}y_d\texttt{:}y_h$, \item Volitelně: Rozsah grafu ve tvaru $x_d\texttt{:}x_h\texttt{:}y_d\texttt{:}y_h$,
kde $x_d$ a~$x_h$ jsou dolní a~horní mez osy $x$ a~$y_d$ a~$y_h$ jsou dolní a~horní mez osy $y$. kde $x_d$ a~$x_h$ jsou dolní a~horní meze osy $x$ a~$y_d$ a~$y_h$ jsou dolní a~horní meze osy $y$.
V případě, že tento argument není uveden, použije se výchozí rozsah $x_d = y_d = -10$ a~$x_h = y_h = 10$. V případě, že tento argument není uveden, použije se výchozí rozsah $x_d = y_d = -10$ a~$x_h = y_h = 10$.
\end{itemize} \end{enumerate}
Návratová hodnota programu odopovídá některému z chybových kódů uvedených v~tabulce~\ref{tab:errors}. Návratová hodnota programu odopovídá některému z chybových kódů uvedených v~tabulce~\ref{tab:errors}.
Při úspěšném vykreslení vznikne soubor se zadaným názvem, který bude obsahovat graf funkce ve formátu PostScript. Při úspěšném vykreslení vznikne soubor se zadaným názvem, který bude obsahovat graf funkce ve formátu PostScript.

View File

@ -1,45 +1,50 @@
#include "errors.h" #include "errors.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
void error_buffer_init(struct error_buffer *eb) { void error_buffer_init(struct error_buffer *eb) {
eb->err = ERR_NO_ERR; eb->err = ERR_NO_ERR;
eb->text_len = 0; eb->text_len = 0;
eb->text[0] = 0; eb->text[0] = 0;
} }
void error_set(struct error_buffer *eb, enum error_code err) { void error_set(struct error_buffer *eb, enum error_code err) {
eb->err = err; eb->err = err;
} }
enum error_code error_get(const struct error_buffer *eb) { enum error_code error_get(const struct error_buffer *eb) {
return eb->err; return eb->err;
} }
void error_printf(struct error_buffer *eb, const char *format, ...) { void error_printf(struct error_buffer *eb, const char *format, ...) {
va_list args; va_list args;
int space = MAX_ERROR_MESSAGE_LENGTH - eb->text_len; /* volné místo v zásobníku */
int write_size; int space = MAX_ERROR_MESSAGE_LENGTH - eb->text_len;
int write_size;
if (space == 0)
return; /* už není volné místo */
if (space == 0)
va_start(args, format); return;
write_size = vsnprintf(eb->text + eb->text_len, MAX_ERROR_MESSAGE_LENGTH - eb->text_len, format, args);
va_end(args); /* zápis */
va_start(args, format);
if (write_size < 0) write_size = vsnprintf(eb->text + eb->text_len, MAX_ERROR_MESSAGE_LENGTH - eb->text_len, format, args);
return; va_end(args);
if (write_size < space) { /* chyba */
eb->text_len += write_size; if (write_size < 0)
} return;
else {
eb->text_len = MAX_ERROR_MESSAGE_LENGTH; /* aktualizace délky */
} if (write_size < space) {
} eb->text_len += write_size;
}
const char *error_get_text(const struct error_buffer *eb) { else {
return eb->text; eb->text_len = MAX_ERROR_MESSAGE_LENGTH;
}
}
const char *error_get_text(const struct error_buffer *eb) {
return eb->text;
} }

132
errors.h
View File

@ -1,67 +1,67 @@
#ifndef ERRORS_H #ifndef ERRORS_H
#define ERRORS_H #define ERRORS_H
#include <stdlib.h> #include <stdlib.h>
#define MAX_ERROR_MESSAGE_LENGTH 512 #define MAX_ERROR_MESSAGE_LENGTH 512
/** /**
* @brief Chybové kódy * @brief Chybové kódy
*/ */
enum error_code { enum error_code {
ERR_NO_ERR = 0, /* Žádná chyba */ ERR_NO_ERR = 0, /* Žádná chyba */
ERR_INVALID_ARGS = 1, /* Neplatné argumenty programu */ ERR_INVALID_ARGS = 1, /* Neplatné argumenty programu */
ERR_INVALID_FUNCTION = 2, /* Zadaná matematická funkce je neplatná */ ERR_INVALID_FUNCTION = 2, /* Zadaná matematická funkce je neplatná */
ERR_INVALID_FILENAME = 3, /* Zadaný název souboru není platný */ ERR_INVALID_FILENAME = 3, /* Zadaný název souboru není platný */
ERR_INVALID_LIMITS = 4, /* Zadané hranice jsou ve špatném formátu */ ERR_INVALID_LIMITS = 4, /* Zadané hranice jsou ve špatném formátu */
ERR_BAD_ALLOC = 5 /* Při alokaci paměti nastala chyba */ ERR_BAD_ALLOC = 5 /* Při alokaci paměti nastala chyba */
}; };
/** /**
* @brief Zásobník pro chybový kód a řetězec popisující chybu * @brief Zásobník pro chybový kód a řetězec popisující chybu
*/ */
struct error_buffer { struct error_buffer {
enum error_code err; enum error_code err;
char text[MAX_ERROR_MESSAGE_LENGTH]; char text[MAX_ERROR_MESSAGE_LENGTH];
size_t text_len; size_t text_len;
}; };
/** /**
* @brief Inicializuje zásobník * @brief Inicializuje zásobník
* *
* @param eb Zásobník * @param eb Zásobník
*/ */
void error_buffer_init(struct error_buffer *eb); void error_buffer_init(struct error_buffer *eb);
/** /**
* @brief Nastaví chybový kód * @brief Nastaví chybový kód
* *
* @param eb Zásobník * @param eb Zásobník
* @param err Chybový kód * @param err Chybový kód
*/ */
void error_set(struct error_buffer *eb, enum error_code err); void error_set(struct error_buffer *eb, enum error_code err);
/** /**
* @brief Přidá do zásobníku formátovaný řetězec * @brief Přidá do zásobníku formátovaný řetězec
* *
* @param eb Zásobník * @param eb Zásobník
*/ */
void error_printf(struct error_buffer *eb, const char *format, ...); void error_printf(struct error_buffer *eb, const char *format, ...);
/** /**
* Vrátí chybový kód * Vrátí chybový kód
* *
* @param eb Zásobník * @param eb Zásobník
* @return Chybový kód * @return Chybový kód
*/ */
enum error_code error_get(const struct error_buffer *eb); enum error_code error_get(const struct error_buffer *eb);
/** /**
* Vrátí řetězec popisující chybu * Vrátí řetězec popisující chybu
* *
* @param eb Zásobník * @param eb Zásobník
* @return Řetězec popisující chybu * @return Řetězec popisující chybu
*/ */
const char *error_get_text(const struct error_buffer *eb); const char *error_get_text(const struct error_buffer *eb);
#endif /* ERRORS_H */ #endif /* ERRORS_H */

26
lex.c
View File

@ -1,4 +1,4 @@
#include "lex.h" #include "lex.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -6,6 +6,7 @@
#include "math_functions.h" #include "math_functions.h"
/* konstanty */
#define CONST_PI 3.141592653589793 #define CONST_PI 3.141592653589793
#define CONST_E 2.718281828459045 #define CONST_E 2.718281828459045
#define CONST_S (1.0 / 9.0) #define CONST_S (1.0 / 9.0)
@ -36,11 +37,14 @@ static void skip_whitespace(struct lexer *lex) {
static int try_advance(struct lexer *lex, const char *str) { static int try_advance(struct lexer *lex, const char *str) {
const char *temp_p = lex->p; const char *temp_p = lex->p;
while (1) { while (1) {
/* pokud jsme se dostali až na konec řetězce, vrátíme 1 */
if (!*str) { if (!*str) {
lex->p = temp_p; lex->p = temp_p;
return 1; return 1;
} }
/* pokud se někde neshoduje, vrátíme 0 */
/* (je zahrnut případ, že str skončí dřív) */
if (*str != *temp_p) { if (*str != *temp_p) {
return 0; return 0;
} }
@ -102,8 +106,14 @@ static int try_fns(struct lexer *lex) {
static int try_number(struct lexer *lex) { static int try_number(struct lexer *lex) {
char *end; char *end;
double val; double val;
/* pokus o přečtení čísla */
val = strtod(lex->p, &end); val = strtod(lex->p, &end);
/* pokud se na aktuální pozici nenacházelo číslo, vrátíme 0 */
if (lex->p == end) return 0; if (lex->p == end) return 0;
/* nastavit token */
lex->tok.type = TOK_NUMBER; lex->tok.type = TOK_NUMBER;
lex->tok.val.num = val; lex->tok.val.num = val;
lex->p = end; lex->p = end;
@ -121,37 +131,51 @@ static int try_constant(struct lexer *lex, const char *name, double val) {
} }
void lex_next(struct lexer *lex) { void lex_next(struct lexer *lex) {
/* přeskočit bílé znaky */
skip_whitespace(lex); skip_whitespace(lex);
/* uložit pozici pro případné vypsání chyby */
/* (v případě úspěšné tokenizace ale chyby syntaxe
by p ukazovalo na další token)*/
lex->prev_p = lex->p; lex->prev_p = lex->p;
/* pokud jsme na konci řetězce, nastavit EOF */
if (!*lex->p) { if (!*lex->p) {
lex->tok.type = TOK_EOF; lex->tok.type = TOK_EOF;
return; return;
} }
/* pokus o rozpoznání tokenů */
/* operátory */
if (try_token(lex, "+", TOK_PLUS)) return; if (try_token(lex, "+", TOK_PLUS)) return;
if (try_token(lex, "-", TOK_MINUS)) return; if (try_token(lex, "-", TOK_MINUS)) return;
if (try_token(lex, "*", TOK_MULTIPLY)) return; if (try_token(lex, "*", TOK_MULTIPLY)) return;
if (try_token(lex, "/", TOK_DIVIDE)) return; if (try_token(lex, "/", TOK_DIVIDE)) return;
if (try_token(lex, "^", TOK_POWER)) return; if (try_token(lex, "^", TOK_POWER)) return;
/* závorky a oddělovač */
if (try_token(lex, "(", TOK_LEFT_PAREN)) return; if (try_token(lex, "(", TOK_LEFT_PAREN)) return;
if (try_token(lex, ")", TOK_RIGHT_PAREN)) return; if (try_token(lex, ")", TOK_RIGHT_PAREN)) return;
if (try_token(lex, ",", TOK_COMMA)) return; if (try_token(lex, ",", TOK_COMMA)) return;
/* symbolické konstanty */
if (try_constant(lex, "pi", CONST_PI)) return; if (try_constant(lex, "pi", CONST_PI)) return;
if (try_constant(lex, "e", CONST_E)) return; if (try_constant(lex, "e", CONST_E)) return;
if (try_constant(lex, "skibidi", CONST_S)) return; /* easter egg */ if (try_constant(lex, "skibidi", CONST_S)) return; /* easter egg */
/* proměnná */
if (try_advance_identifier(lex, lex->variable_name)) { if (try_advance_identifier(lex, lex->variable_name)) {
lex->tok.type = TOK_VARIABLE; lex->tok.type = TOK_VARIABLE;
return; return;
} }
/* funkce */
if (try_fns(lex)) return; if (try_fns(lex)) return;
/* číselná konstanta */
if (try_number(lex)) return; if (try_number(lex)) return;
/* možnosti vyčerpány => chyba */
error_set(&lex->eb, ERR_INVALID_FUNCTION); error_set(&lex->eb, ERR_INVALID_FUNCTION);
error_printf(&lex->eb, "Unrecognized sequence \"%s\"\n", lex->p); error_printf(&lex->eb, "Unrecognized sequence \"%s\"\n", lex->p);
lex_print_position(lex, &lex->eb); lex_print_position(lex, &lex->eb);

4
lex.h
View File

@ -1,10 +1,8 @@
#ifndef LEX_H #ifndef LEX_H
#define LEX_H #define LEX_H
#include "errors.h" #include "errors.h"
#define LEX_DEBUG
/** /**
* @brief Typ tokenu * @brief Typ tokenu
*/ */

13
main.c
View File

@ -13,13 +13,16 @@ static void print_usage(FILE *f, const char *name) {
} }
/* přečte z řetězce číslo a posune ukazatel */
static int read_double(const char **str, double *out) { static int read_double(const char **str, double *out) {
char *end; char *end;
*out = strtod(*str, &end); *out = strtod(*str, &end);
/* pokud se na aktuální pozici nenacházelo číslo, vrátíme 0 */
if (*str == end) if (*str == end)
return 0; return 0;
/* posuneme ukazatel za číslo */
*str = end; *str = end;
return 1; return 1;
} }
@ -32,6 +35,7 @@ static int read_double(const char **str, double *out) {
if (*str++ != c) \ if (*str++ != c) \
return 0; return 0;
/* Zpracuje rozsah vykreslení grafu ve formátu "xmin:xmax:ymin:ymax" */
static int parse_range(const char *str, struct graph_range *range) { static int parse_range(const char *str, struct graph_range *range) {
EXPECT_DOUBLE(str, &range->xmin); EXPECT_DOUBLE(str, &range->xmin);
EXPECT_CHAR(str, ':'); EXPECT_CHAR(str, ':');
@ -44,6 +48,7 @@ static int parse_range(const char *str, struct graph_range *range) {
return 1; return 1;
} }
/* vygeneruje graf funkce do souboru */
static enum error_code export_to_file(const struct expr_node *node, const struct graph_range *range, const char *function, const char *out_name) { static enum error_code export_to_file(const struct expr_node *node, const struct graph_range *range, const char *function, const char *out_name) {
FILE *file; FILE *file;
@ -59,6 +64,7 @@ static enum error_code export_to_file(const struct expr_node *node, const struct
} }
#ifdef ENABLE_GRAPHVIZ_EXPORT #ifdef ENABLE_GRAPHVIZ_EXPORT
/* vygeneruje strom výrazu v .dot formátu pro ladění */
static void export_gv(const struct expr_node *node, const char *out_name) { static void export_gv(const struct expr_node *node, const char *out_name) {
FILE *file; FILE *file;
@ -79,11 +85,13 @@ int main(int argc, char *argv[]) {
struct expr_node *node; struct expr_node *node;
struct graph_range range; struct graph_range range;
/* kontrola argumentů */
if (argc < 3) { if (argc < 3) {
print_usage(stderr, argv[0]); print_usage(stderr, argv[0]);
return ERR_INVALID_ARGS; return ERR_INVALID_ARGS;
} }
/* zpracování rozsahu */
if (argc > 3) { if (argc > 3) {
if (!parse_range(argv[3], &range)) { if (!parse_range(argv[3], &range)) {
fprintf(stderr, "Invalid format of ranges! The correct format is \"xMin:xMax:yMin:yMax\".\n"); fprintf(stderr, "Invalid format of ranges! The correct format is \"xMin:xMax:yMin:yMax\".\n");
@ -97,20 +105,25 @@ int main(int argc, char *argv[]) {
range.ymax = 10.0; range.ymax = 10.0;
} }
/* zpracování výrazu */
node = parser_parse(&parser, argv[1], "x"); node = parser_parse(&parser, argv[1], "x");
if (!node) { if (!node) {
/* vypsání chyby */
fprintf(stderr, "%s", parser_get_error_text(&parser)); fprintf(stderr, "%s", parser_get_error_text(&parser));
return parser_get_error(&parser); return parser_get_error(&parser);
} }
/* vygenerování grafu */
err = export_to_file(node, &range, argv[1], argv[2]); err = export_to_file(node, &range, argv[1], argv[2]);
#ifdef ENABLE_GRAPHVIZ_EXPORT #ifdef ENABLE_GRAPHVIZ_EXPORT
export_gv(node, "graph.dot"); export_gv(node, "graph.dot");
#endif /* ENABLE_GRAPHVIZ_EXPORT */ #endif /* ENABLE_GRAPHVIZ_EXPORT */
/* uvolnění paměti */
node_free(node); node_free(node);
/* návratový kód */
return err; return err;
} }

View File

@ -3,6 +3,7 @@
#include <math.h> #include <math.h>
#include <errno.h> #include <errno.h>
/* Vytvoří vyhodnocovač matematické funkce s jedním argumentem */
#define MAKE_FUNCTION_1_ARG(name) \ #define MAKE_FUNCTION_1_ARG(name) \
static enum eval_result mf_##name(double *y, const double *args) { \ static enum eval_result mf_##name(double *y, const double *args) { \
errno = 0; \ errno = 0; \

View File

@ -1,4 +1,4 @@
#ifndef MATH_FUNCTIONS_H #ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H #define MATH_FUNCTIONS_H
#include <stdlib.h> #include <stdlib.h>

View File

@ -48,8 +48,10 @@ static void error_expected_tokens(struct parser *parser, size_t num_tokens, ...)
size_t i; size_t i;
va_list ap; va_list ap;
/* vyskytující se token */
enum token_type got = get_token(parser)->type; enum token_type got = get_token(parser)->type;
/* chyba při lexikální analýze */
if (got == TOK_ERROR) { if (got == TOK_ERROR) {
error_set(&parser->eb, lex_get_error(&parser->lexer)); error_set(&parser->eb, lex_get_error(&parser->lexer));
error_printf(&parser->eb, "%s", lex_get_error_text(&parser->lexer)); error_printf(&parser->eb, "%s", lex_get_error_text(&parser->lexer));
@ -59,6 +61,7 @@ static void error_expected_tokens(struct parser *parser, size_t num_tokens, ...)
error_set(&parser->eb, ERR_INVALID_FUNCTION); error_set(&parser->eb, ERR_INVALID_FUNCTION);
error_printf(&parser->eb, "Syntax error - expected "); error_printf(&parser->eb, "Syntax error - expected ");
/* výpis očekávaných tokenů */
va_start(ap, num_tokens); va_start(ap, num_tokens);
for (i = 0; i < num_tokens; ++i) { for (i = 0; i < num_tokens; ++i) {
enum token_type tok = va_arg(ap, enum token_type); enum token_type tok = va_arg(ap, enum token_type);
@ -70,8 +73,10 @@ static void error_expected_tokens(struct parser *parser, size_t num_tokens, ...)
} }
va_end(ap); va_end(ap);
/* výpis vyskytlého se tokenu */
error_printf(&parser->eb, " - but got %s\n", lex_token_str(got)); error_printf(&parser->eb, " - but got %s\n", lex_token_str(got));
/* výpis aktuální pozice ve vstupním řetězci */
lex_print_position(&parser->lexer, &parser->eb); lex_print_position(&parser->lexer, &parser->eb);
} }
@ -82,14 +87,17 @@ static int parse_n_expressions(struct parser *parser, struct expr_node **out_nod
static struct expr_node *parse_bracketed(struct parser *parser) { static struct expr_node *parse_bracketed(struct parser *parser) {
struct expr_node *node; struct expr_node *node;
/* požadovat levou závorku */
if (!accept_token(parser, TOK_LEFT_PAREN)) { if (!accept_token(parser, TOK_LEFT_PAREN)) {
error_expected_tokens(parser, 1, TOK_LEFT_PAREN); error_expected_tokens(parser, 1, TOK_LEFT_PAREN);
return NULL; return NULL;
} }
/* zpracovat výraz v závorkach */
if (!(node = parse_expression(parser))) if (!(node = parse_expression(parser)))
return NULL; return NULL;
/* požadovat pravou závorku */
if (!accept_token(parser, TOK_RIGHT_PAREN)) { if (!accept_token(parser, TOK_RIGHT_PAREN)) {
error_expected_tokens(parser, 1, TOK_RIGHT_PAREN); error_expected_tokens(parser, 1, TOK_RIGHT_PAREN);
node_free(node); node_free(node);
@ -103,28 +111,35 @@ static struct expr_node *parse_function(struct parser *parser) {
struct expr_node *node; struct expr_node *node;
struct expr_node *arg_nodes[MAX_MATH_FUNCTION_ARGS]; struct expr_node *arg_nodes[MAX_MATH_FUNCTION_ARGS];
size_t i; size_t i;
/* funkce */
size_t fn_idx = token_fn_idx(parser); size_t fn_idx = token_fn_idx(parser);
const struct math_function *fn = &fns_get()[fn_idx]; const struct math_function *fn = &fns_get()[fn_idx];
next_token(parser); next_token(parser);
/* požadovat levou závorku */
if (!accept_token(parser, TOK_LEFT_PAREN)) { if (!accept_token(parser, TOK_LEFT_PAREN)) {
error_expected_tokens(parser, 1, TOK_LEFT_PAREN); error_expected_tokens(parser, 1, TOK_LEFT_PAREN);
return NULL; return NULL;
} }
/* zpracovat argumenty funkce */
if (!parse_n_expressions(parser, arg_nodes, fn->num_args)) if (!parse_n_expressions(parser, arg_nodes, fn->num_args))
return NULL; return NULL;
/* vytvořit uzel funkce */
if (!(node = node_create_fn(fn_idx, arg_nodes))) { if (!(node = node_create_fn(fn_idx, arg_nodes))) {
error_bad_alloc(parser); error_bad_alloc(parser);
/* uvolnit všechny uzly argumentů */
for (i = 0; i < fn->num_args; ++i) for (i = 0; i < fn->num_args; ++i)
node_free(arg_nodes[i]); node_free(arg_nodes[i]);
return NULL; return NULL;
} }
/* požadovat pravou závorku */
if (!accept_token(parser, TOK_RIGHT_PAREN)) { if (!accept_token(parser, TOK_RIGHT_PAREN)) {
error_expected_tokens(parser, 1, TOK_RIGHT_PAREN); error_expected_tokens(parser, 1, TOK_RIGHT_PAREN);
node_free(node); node_free(node);
@ -138,6 +153,7 @@ static struct expr_node *parse_function(struct parser *parser) {
static struct expr_node *parse_factor(struct parser *parser) { static struct expr_node *parse_factor(struct parser *parser) {
struct expr_node *node; struct expr_node *node;
/* konstanta */
if (token_is(parser, TOK_NUMBER)) { if (token_is(parser, TOK_NUMBER)) {
double val = token_num(parser); double val = token_num(parser);
next_token(parser); next_token(parser);
@ -149,6 +165,7 @@ static struct expr_node *parse_factor(struct parser *parser) {
return node; return node;
} }
/* proměnná */
if (accept_token(parser, TOK_VARIABLE)) { if (accept_token(parser, TOK_VARIABLE)) {
if (!(node = node_create_x())) { if (!(node = node_create_x())) {
error_bad_alloc(parser); error_bad_alloc(parser);
@ -157,10 +174,12 @@ static struct expr_node *parse_factor(struct parser *parser) {
return node; return node;
} }
/* funkce */
if (token_is(parser, TOK_FUNCTION)) { if (token_is(parser, TOK_FUNCTION)) {
return parse_function(parser); return parse_function(parser);
} }
/* výraz obalený závorkami */
if (token_is(parser, TOK_LEFT_PAREN)) { if (token_is(parser, TOK_LEFT_PAREN)) {
return parse_bracketed(parser); return parse_bracketed(parser);
} }
@ -175,15 +194,19 @@ static struct expr_node *parse_unary(struct parser *parser);
static struct expr_node *parse_power(struct parser *parser) { static struct expr_node *parse_power(struct parser *parser) {
struct expr_node *node, *new_node, *inner; struct expr_node *node, *new_node, *inner;
/* základ (nebo výsledek pokud nenásleduje umocnění) */
if (!(node = parse_factor(parser))) if (!(node = parse_factor(parser)))
return NULL; return NULL;
/* umocnění? */
if (accept_token(parser, TOK_POWER)) { if (accept_token(parser, TOK_POWER)) {
/* exponent */
if (!(inner = parse_unary(parser))) { if (!(inner = parse_unary(parser))) {
node_free(node); node_free(node);
return NULL; return NULL;
} }
/* vytvořit uzel umocnění */
if (!(new_node = node_create_pow(node, inner))) { if (!(new_node = node_create_pow(node, inner))) {
error_bad_alloc(parser); error_bad_alloc(parser);
node_free(node); node_free(node);
@ -199,12 +222,15 @@ static struct expr_node *parse_power(struct parser *parser) {
/* Zpracuje unární mínus, resp. plus */ /* Zpracuje unární mínus, resp. plus */
static struct expr_node *parse_unary(struct parser *parser) { static struct expr_node *parse_unary(struct parser *parser) {
/* unární mínus? */
if (accept_token(parser, TOK_MINUS)) { if (accept_token(parser, TOK_MINUS)) {
struct expr_node *node, *inner; struct expr_node *node, *inner;
/* operand */
if (!(inner = parse_power(parser))) if (!(inner = parse_power(parser)))
return NULL; return NULL;
/* vytvořit uzel negace */
if (!(node = node_create_neg(inner))) { if (!(node = node_create_neg(inner))) {
error_bad_alloc(parser); error_bad_alloc(parser);
node_free(inner); node_free(inner);
@ -214,7 +240,9 @@ static struct expr_node *parse_unary(struct parser *parser) {
return node; return node;
} }
/* unární plus - nehraje roli */
accept_token(parser, TOK_PLUS); accept_token(parser, TOK_PLUS);
return parse_power(parser); return parse_power(parser);
} }
@ -222,12 +250,17 @@ static struct expr_node *parse_unary(struct parser *parser) {
static struct expr_node *parse_term(struct parser *parser) { static struct expr_node *parse_term(struct parser *parser) {
struct expr_node *node, *new_node, *inner; struct expr_node *node, *new_node, *inner;
/* výraz před prvním operátorem */
if (!(node = parse_unary(parser))) if (!(node = parse_unary(parser)))
return NULL; return NULL;
/* iterovat dokud se za výrazy nachází operátory součinu nebo dělení */
/* bylo by možné "vyřešit" rekurzí, ale není nutné */
while (1) { while (1) {
/* funkce pro vytvoření uzlu binární operace na základě operátoru */
struct expr_node *(*create_node)(struct expr_node *left, struct expr_node *right); struct expr_node *(*create_node)(struct expr_node *left, struct expr_node *right);
/* rozhodnout se na základě operátoru, případně ukončit smyčku */
if (accept_token(parser, TOK_MULTIPLY)) if (accept_token(parser, TOK_MULTIPLY))
create_node = node_create_mult; create_node = node_create_mult;
else if (accept_token(parser, TOK_DIVIDE)) else if (accept_token(parser, TOK_DIVIDE))
@ -235,11 +268,13 @@ static struct expr_node *parse_term(struct parser *parser) {
else else
break; break;
/* zpracovat další výraz */
if (!(inner = parse_unary(parser))) { if (!(inner = parse_unary(parser))) {
node_free(node); node_free(node);
return NULL; return NULL;
} }
/* vytvořit uzel pro danou operaci */
if (!(new_node = create_node(node, inner))) { if (!(new_node = create_node(node, inner))) {
error_bad_alloc(parser); error_bad_alloc(parser);
node_free(node); node_free(node);
@ -247,6 +282,7 @@ static struct expr_node *parse_term(struct parser *parser) {
return NULL; return NULL;
} }
/* uzly postupně řetězit */
node = new_node; node = new_node;
} }
@ -257,12 +293,17 @@ static struct expr_node *parse_term(struct parser *parser) {
static struct expr_node *parse_expression(struct parser *parser) { static struct expr_node *parse_expression(struct parser *parser) {
struct expr_node *node, *new_node, *inner; struct expr_node *node, *new_node, *inner;
/* výraz před prvním operátorem */
if (!(node = parse_term(parser))) if (!(node = parse_term(parser)))
return NULL; return NULL;
/* iterovat dokud se za výrazy nachází operátory sčítání nebo odčítání */
/* bylo by možné "vyřešit" rekurzí, ale není nutné */
while (1) { while (1) {
/* funkce pro vytvoření uzlu binární operace na základě operátoru */
struct expr_node *(*create_node)(struct expr_node *left, struct expr_node *right); struct expr_node *(*create_node)(struct expr_node *left, struct expr_node *right);
/* rozhodnout se na základě operátoru, případně ukončit smyčku */
if (accept_token(parser, TOK_PLUS)) if (accept_token(parser, TOK_PLUS))
create_node = node_create_add; create_node = node_create_add;
else if (accept_token(parser, TOK_MINUS)) else if (accept_token(parser, TOK_MINUS))
@ -270,11 +311,13 @@ static struct expr_node *parse_expression(struct parser *parser) {
else else
break; break;
/* zpracovat další výraz */
if (!(inner = parse_term(parser))) { if (!(inner = parse_term(parser))) {
node_free(node); node_free(node);
return NULL; return NULL;
} }
/* vytvořit uzel pro danou operaci */
if (!(new_node = create_node(node, inner))) { if (!(new_node = create_node(node, inner))) {
error_bad_alloc(parser); error_bad_alloc(parser);
node_free(node); node_free(node);
@ -282,6 +325,7 @@ static struct expr_node *parse_expression(struct parser *parser) {
return NULL; return NULL;
} }
/* uzly postupně řetězit */
node = new_node; node = new_node;
} }
@ -299,49 +343,65 @@ static int parse_n_expressions(struct parser *parser, struct expr_node **out_nod
out_nodes[i] = node; out_nodes[i] = node;
/* akceptovat čárku jakožto oddělovač výrazů */
if (i < n - 1) if (i < n - 1)
accept_token(parser, TOK_COMMA); accept_token(parser, TOK_COMMA);
} }
/* nebyly zpracovány všechny výrazy? */
if (i != n) { if (i != n) {
/* uvolnit všechny doposud alokované uzly */
while (i) { while (i) {
--i; --i;
node_free(out_nodes[i]); node_free(out_nodes[i]);
out_nodes[i] = NULL; out_nodes[i] = NULL;
} }
/* chyba */
return 0; return 0;
} }
/* úspěch */
return 1; return 1;
} }
int parser_parse_n(struct parser *parser, const char *str, const char *variable_name, struct expr_node **out_nodes, size_t n) { int parser_parse_n(struct parser *parser, const char *str, const char *variable_name, struct expr_node **out_nodes, size_t n) {
/* inicializace chybového bufferu */
error_buffer_init(&parser->eb); error_buffer_init(&parser->eb);
/* inicializace lexikálního analyzázoru */
lex_init(&parser->lexer, str, variable_name); lex_init(&parser->lexer, str, variable_name);
/* zpracování n tokenů */
if (!parse_n_expressions(parser, out_nodes, n)) { if (!parse_n_expressions(parser, out_nodes, n)) {
return 0; return 0;
} }
/* požadovat konec vstupu */
if (!token_is(parser, TOK_EOF)) { if (!token_is(parser, TOK_EOF)) {
size_t i; size_t i;
error_expected_tokens(parser, 1, TOK_EOF); error_expected_tokens(parser, 1, TOK_EOF);
/* uvolnit všechny uzly */
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
node_free(out_nodes[i]); node_free(out_nodes[i]);
out_nodes[i] = NULL; out_nodes[i] = NULL;
} }
/* chyba */
return 0; return 0;
} }
/* úspěch */
return 1; return 1;
} }
struct expr_node *parser_parse(struct parser *parser, const char *str, const char *variable_name) { struct expr_node *parser_parse(struct parser *parser, const char *str, const char *variable_name) {
struct expr_node *out_node = NULL; struct expr_node *out_node = NULL;
/* zpracování jednoho výrazu */
parser_parse_n(parser, str, variable_name, &out_node, 1); parser_parse_n(parser, str, variable_name, &out_node, 1);
return out_node; return out_node;
} }

View File

@ -1,4 +1,4 @@
#ifndef PARSER_H #ifndef PARSER_H
#define PARSER_H #define PARSER_H
#include "tree.h" #include "tree.h"

View File

@ -11,4 +11,4 @@ struct graph_range {
void ps_generate_graph(FILE *file, const struct expr_node *node, const struct graph_range *range, const char *function); void ps_generate_graph(FILE *file, const struct expr_node *node, const struct graph_range *range, const char *function);
#endif #endif /* PS_GRAPH_H */

7
tree.c
View File

@ -4,6 +4,7 @@
#include <errno.h> #include <errno.h>
#include "tree.h" #include "tree.h"
/* alokuje nový uzel */
static struct expr_node *alloc_node(void) { static struct expr_node *alloc_node(void) {
return malloc(sizeof(struct expr_node)); return malloc(sizeof(struct expr_node));
} }
@ -24,6 +25,7 @@ struct expr_node *node_create_neg(struct expr_node *unop) {
return node; return node;
} }
/* vytvoří uzel binární operace */
static struct expr_node *create_binary_node(enum expr_type type, struct expr_node *left, struct expr_node *right) { static struct expr_node *create_binary_node(enum expr_type type, struct expr_node *left, struct expr_node *right) {
struct expr_node *node = alloc_node(); struct expr_node *node = alloc_node();
if (!node) return NULL; if (!node) return NULL;
@ -75,12 +77,15 @@ struct expr_node *node_create_fn(size_t fn_idx, struct expr_node **args) {
return node; return node;
} }
/* pro naše účely definované "nekonečno" */
#define INF (1.0e256) #define INF (1.0e256)
/* zkontroluje, zda je číslo reálné */
static int is_real(double x) { static int is_real(double x) {
return x == x && x < INF && x > -INF; return x == x && x < INF && x > -INF;
} }
/* vrátí výsledek vyhodnocení na základě hodnoty x */
static enum eval_result check_real(double x) { static enum eval_result check_real(double x) {
if (is_real(x)) if (is_real(x))
return EVAL_OK; return EVAL_OK;
@ -88,8 +93,6 @@ static enum eval_result check_real(double x) {
return EVAL_ERROR; return EVAL_ERROR;
} }
#define RETURN_CHECK_REAL(x) if (is_real((x))) return EVAL_OK; else return EVAL_ERROR;
#define EVAL_CHECK(node, x, y) if (node_eval((node), (x), (y)) != EVAL_OK) return EVAL_ERROR; #define EVAL_CHECK(node, x, y) if (node_eval((node), (x), (y)) != EVAL_OK) return EVAL_ERROR;
enum eval_result node_eval(const struct expr_node *node, double x, double *y) { enum eval_result node_eval(const struct expr_node *node, double x, double *y) {

2
tree.h
View File

@ -1,4 +1,4 @@
#ifndef TREE_H #ifndef TREE_H
#define TREE_H #define TREE_H
#include "lex.h" #include "lex.h"