diff --git a/coursework/report/report.tex b/coursework/report/report.tex index bc9e61e..7c038cc 100644 --- a/coursework/report/report.tex +++ b/coursework/report/report.tex @@ -209,15 +209,169 @@ \end{enumerate} - \newpage - \section {Математическое описание} + % \newpage + % \section {Математическое описание} \newpage - \section {Особенности реализации} + \section{Особенности реализации} + Согласно заданию для каждой части работы был создан отдельный проект \texttt{stack}. + \subsection{Часть 1: Синтаксический анализ арифметических выражений} + + \subsubsection{Тип Parser} + Тип \texttt{Parser} обеспечивает разбор входной строки по заданным правилам. Он принимает на вход список токенов (например, символов) и пытается разобрать их в соответствии с описанными правилами, возвращая либо результат с оставшейся частью строки, либо \texttt{Nothing}, если разбор не удался. + + Код типа \texttt{Parser} представлен в листинге~\ref{lst:parser_type}. + \begin{itemize} + \item Вход: список токенов. + \item Выход: результат разбора в виде \texttt{Maybe ([tok], a)}. + \end{itemize} + + Для типа \texttt{Parser} определены представители классов типов для \texttt{Functor}, \texttt{Applicative} и \texttt{Alternative}. Представитель \texttt{Functor} позволяет применять функцию к результату разбора парсера. Представители \texttt{Applicative} и \texttt{Alternative} позволяют последовательно комбинировать разные парсеры и функции и составлять сложные парсеры из простых. + +\begin{lstlisting}[caption={Определение типа Parser и его представителей для классов типов Functor, Applicative и Alternative.}, label={lst:parser_type}] +newtype Parser tok a = + Parser { runParser :: [tok] -> Maybe ([tok], a)} + +instance Functor (Parser tok) where + fmap g (Parser p) = Parser $ \xs -> + case p xs of + Nothing -> Nothing + Just (cs, c) -> Just (cs, g c) + +instance Applicative (Parser tok) where + pure x = Parser $ \toks -> Just (toks, x) + Parser u <*> Parser v = Parser $ \xs -> + case u xs of + Nothing -> Nothing + Just (xs', g) -> + case v xs' of + Nothing -> Nothing + Just (xs'', x) -> Just (xs'', g x) + +instance Alternative (Parser tok) where + empty = Parser $ \_ -> Nothing + Parser u <|> Parser v = Parser $ \xs -> + case u xs of + Nothing -> v xs + z -> z +\end{lstlisting} + + \subsubsection{Работа с арифметическими операциями} + В листинге~\ref{lst:Operation} представлен код определения класса \texttt{Operation}, а также нескольких вспомогательных функций для работы с ним. Тип используется для хранения одной из четырёх арифметических операций: сложение, вычитание, умножение и деление. Функция \texttt{operationToString} принимает значение типа \texttt{Operation} и возвращает его строковое представление. Функция \texttt{operationToOperator} также принимает значение типа \texttt{Operation}, а возвращает функцию, соответствующую арифметической операции. + +\begin{lstlisting}[caption={Определение типа Operation и функций для работы с ним.}, label={lst:Operation}] +data Operation = Add | Sub | Mul | Div deriving Show + +operationToString :: Operation -> String +operationToString op = case op of + Add -> "+" + Sub -> "-" + Mul -> "*" + Div -> "/" + +operationToOperator :: Operation -> (Int -> Int -> Int) +operationToOperator op = case op of + Add -> (+) + Sub -> (-) + Mul -> (*) + Div -> div +\end{lstlisting} + + \subsubsection{Базовые парсеры} + + В этом разделе рассматриваются основные парсеры, используемые для разбора арифметических выражений. Эти парсеры являются строительными блоками для более сложных выражений. Их код представлен в листинге~\ref{lst:base_parsers}. + + \begin{itemize} + \item \texttt{satisfy} — парсит символ, удовлетворяющий предикату, и возвращает его. + \item \texttt{char} — парсит один заданный символ и возвращает его. + \item \texttt{digit} — парсит одну цифру и возвращает в виде символа. + \item \texttt{skipSpaces} — парсит все пробелы пока не встретить символ, который пробелом не является. + \item \texttt{number} — парсит целое число (последовательность цифр) и возвращает в виде~\texttt{Int}. + \item \texttt{operation} — парсит арифметическую операцию (\texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}) и возвращает как значение типа \texttt{Operation}. + \end{itemize} + +\begin{lstlisting}[caption={Базовые парсеры}, label={lst:base_parsers}] +satisfy :: (tok -> Bool) -> Parser tok tok +satisfy pr = Parser $ \case + (c:cs) | pr c -> Just (cs, c) + _ -> Nothing + +char :: Char -> Parser Char Char +char c = satisfy (== c) + +digit :: Parser Char Char +digit = satisfy isDigit + +skipSpaces :: Parser Char String +skipSpaces = many (char ' ') + +number :: Parser Char Int +number = skipSpaces *> (strToInt <$> some digit) + where + strToInt = foldl (\acc x -> acc * 10 + digitToInt x) 0 + +operation :: Parser Char Operation +operation = skipSpaces *> ( + char '+' *> pure Add <|> + char '-' *> pure Sub <|> + char '*' *> pure Mul <|> + char '/' *> pure Div + ) +\end{lstlisting} + + \subsubsection{Парсер expression} + Парсер \texttt{expression}, код которого представлен в листинге~\ref{lst:expression}, парсит выражение вида \texttt{<число> <операция> <число>}. В случае успеха возвращает кортеж вида: \texttt{(Int -- левый операнд, Operation -- операция, Int -- правый операнд)}. Является комбинацей парсеров \texttt{number} и \texttt{operation}. Не чувствителен к пробелам до выражения и внутри него, между операндами и оператором. Поглощает также пробелы после выражения с помощью парсера \texttt{skipSpaces}. + +\begin{lstlisting}[caption={Код функции expression}, label={lst:expression}] +expression :: Parser Char (Int, Operation, Int) +expression = (,,) <$> number <*> operation <*> number <* skipSpaces +\end{lstlisting} + + \subsubsection{Функция processExpression} + Код функции \texttt{processExpression} представлен в листинге~\ref{lst:processExpression}. + Функция принимает строку, парсит её как выражение, вычисляет результат и возвращает строку с ответом. При ошибке парсинга генерирует ошибку. + + Вход: \texttt{String} — строка с выражением. + Выход: \texttt{String} — результат вычисления в формате \texttt{a op b = result}. + + Вспомогательная функция \texttt{calculateExpression} используется для вычисления результата. На вход она получает операнды и операцию, а возвращает вычисленное значение. Её код также представлен в листинге~\ref{lst:processExpression}. + +\begin{lstlisting}[caption={Код функции processExpression}, label={lst:processExpression}] +processExpression :: String -> String +processExpression s = case runParser expression s of + Nothing -> error $ "Не удалось прочитать выражение: \"" ++ s ++ "\"" + Just (cs, (a, op, b)) -> case cs of + [] -> show a ++ " " ++ operationToString op ++ " " ++ + show b ++ " = " ++ show (calculateExpression (a, op, b)) ++ "\n" + _ -> error $ "Не удалось прочитать выражение: \"" ++ s ++ "\"" + + +calculateExpression :: (Int, Operation, Int) -> Int +calculateExpression (a, op, b) = (operationToOperator op) a b +\end{lstlisting} + + + \subsubsection{Функция main} + Код функции \texttt{main} представлен в листинге~\ref{lst:main}. + Функция \texttt{main} считывает имя файла у пользователя, читает файл, построчно обрабатывает каждое выражение с помощью \texttt{processExpression} и выводит результат. + + \begin{lstlisting}[caption={Код функции main}, label={lst:main}] + main :: IO () + main = + putStrLn "Введите имя файла:" >> + getLine >>= \fileName -> + readFile fileName >>= \content -> + let expressions = lines content in + putStrLn $ concatMap processExpression expressions + \end{lstlisting} + + + \subsection{Часть 2: Синтаксический анализ текста и генерация фраз} +