2016-11-23 40 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