Особенности реализации первой части в отчёте
This commit is contained in:
@@ -209,15 +209,169 @@
|
|||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
|
|
||||||
\newpage
|
% \newpage
|
||||||
\section {Математическое описание}
|
% \section {Математическое описание}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\newpage
|
\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: Синтаксический анализ текста и генерация фраз}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user