2016-11-23 37 views
2

我的目标是使用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 

我没有问题,结合我目前拥有的解析器,使用chainl1chainr1,实施正确的顺序结合性和优先级,但是我不知道如何正确实施否定和括号。

我知道如果否定符号也是-符号,它可以在整数,括号或变量(字母串)之前出现。变量是计算器的另一部分(我没有麻烦)。此外,否定可以包括空白;例如1+2* - 3是该计算器的有效字符串输入。

+0

“PEMDAS” 是不是一个真正的字。你的意思是“操作顺序”? – dfeuer

+0

是的,操作顺序。 – Harambe17

+0

Hello CS 161通过搜索发现此页的学生,1.不要在线发布作业。 2.不要依靠SO贡献者为你做功课。如果遇到麻烦,请在Piazza上张贴。这就是我们使用它的原因。并熟悉协作政策:http://cmsc-16100.cs.uchicago.edu/2017/policies.php我们期待您提交的任何解决方案能够展示与作业的原创互动。如果您有任何问题,请给我们发邮件。和平,你的教师 –

回答

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)))) 
+0

还有一个问题:如果我还想实现一个解析器,它可以读取以下字符串:“put(x,y,zee)=(1,2,3)for x + y * zee” “put”和“for”表示我正在声明/使用变量,我希望这个解析为'[“x”,“y”,“zee”] [Number 1,Number 2,Number 3](Plus (Var“x)(Mult(Var”y)(Var“zee”)))'我该如何去做这件事? – Harambe17

+0

首先,您必须针对您的语法为您的任务设计抽象语法树(AST)。然后,你可以考虑AST的分析。我不明白你的例子的语法和语义,这就是为什么我不能帮你更多。 – freestyle