我的目标是使用Text.ParserCombinators.ReadP在Haskell中构建一个计算器,它实现+, - ,*,/,^,以及否定,并遵循PEMDAS。首先,我必须将字符串输入解析为数学表达式(MathExp
)。我的部分代码如下:
import Control.Applicative hiding (many)
import Control.Monad
import Data.Char
import Data.List
import Data.Ord
import Text.ParserCombinators.Readp
type Name = String
type Number = Int
data MathExp
= Number Number
| Neg MathExp
| Plus MathExp MathExp
| Minus MathExp MathExp
| Mult MathExp MathExp
| Div MathExp MathExp
| Pow MathExp MathExp
| Var String
deriving (Eq, Show)
parseNumber :: ReadP MathExp
parseNumber = do
skipSpaces
x <- munch1 isDigit
return (Number (read x :: Int))
parsePlus :: ReadP (MathExp -> MathExp -> MathExp)
parsePlus = do
x <- char '+'
return Plus
parseMinus :: ReadP (MathExp -> MathExp -> MathExp)
parseMinus = do
skipSpaces
x <- char '-'
return Minus
parsePlusMinus = choice [parsePlus, parseMinus] --parse both--
parseMult :: ReadP (MathExp -> MathExp -> MathExp)
parseMult = do
x <- char '*'
return Mult
parseDiv :: ReadP (MathExp -> MathExp -> MathExp)
parseDiv = do
x <- char '/'
return Div
parseMultDiv = choice [parseMult, parseDiv] --parse both M,D--
parsePow :: ReadP (MathExp -> MathExp -> MathExp)
parsePow = do
x <- char '^'
return Pow
parseNeg :: ReadP MathExp
parseNeg = undefined
parseParens = undefined
我使用chainl1
和chainr1
组合我目前拥有的解析器没有问题,以正确的顺序实现关联性和优先级,但我不知道如何正确实现否定和括号。
我知道如果该否定也是-
符号,它可以在整数,括号或变量(字母串)之前出现。变量是计算器的另一部分(我没有遇到麻烦)。此外,否定可以包括空白区域;例如1+2* - 3
是此计算器的有效字符串输入。
答案 0 :(得分:3)
你可以这样做:
import Control.Applicative
import Data.Char
import Text.ParserCombinators.ReadP
type Name = String
type Number = Int
data MathExp
= Number Number
| Neg MathExp
| Plus MathExp MathExp
| Minus MathExp MathExp
| Mult MathExp MathExp
| Div MathExp MathExp
| Pow MathExp MathExp
| Var Name
deriving (Eq, Show)
runParser :: ReadP a -> String -> Maybe a
runParser p s =
case readP_to_S p s of
[(x, [])] -> Just x
_ -> Nothing
mathExp = mathExp' <* skipSpaces <* eof
mathExp' = term `chainl1` addop
term = factor `chainl1` mulop
factor = expo `chainr1` expop
expo = number <|> var <|> parens mathExp' <|> neg expo
number = skipSpaces *> (Number . read <$> munch1 isDigit)
var = skipSpaces *> (Var <$> munch1 isAlpha)
addop = skipSpaces *> (Plus <$ char '+' <|> Minus <$ char '-')
mulop = skipSpaces *> (Mult <$ char '*' <|> Div <$ char '/')
expop = skipSpaces *> (Pow <$ char '^')
neg p = skipSpaces *> (Neg <$> (char '-' *> p))
parens = between (skipSpaces *> char '(') (skipSpaces *> char ')')
main = print $ runParser mathExp "1+2* - 3"
输出将是:
Just (Plus (Number 1) (Mult (Number 2) (Neg (Number 3))))