Last active
August 29, 2015 14:07
-
-
Save hughfdjackson/5a54eefd59400c3dff50 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Text.Parsec | |
import Text.Parsec.String | |
import Control.Applicative hiding (many, (<|>)) | |
data Calc = Number Float | |
| OpPlus Calc Calc | |
| OpMult Calc Calc | |
| OpSubt Calc Calc | |
| OpDivd Calc Calc | |
instance Show Calc where | |
show (Number a) = show a | |
show (OpPlus c c') = "(" ++ show c ++ " + " ++ show c' ++ ")" | |
show (OpSubt c c') = "(" ++ show c ++ " - " ++ show c' ++ ")" | |
show (OpMult c c') = "(" ++ show c ++ " * " ++ show c' ++ ")" | |
show (OpDivd c c') = "(" ++ show c ++ " / " ++ show c' ++ ")" | |
-- INTERPRETING | |
intr :: String -> Either ParseError Float | |
intr str = eval <$> parse calc "(unknown)" str | |
-- EVALUATING | |
eval :: Calc -> Float | |
eval (Number a) = a | |
eval (OpPlus c c') = (eval c) + (eval c') | |
eval (OpSubt c c') = (eval c) - (eval c') | |
eval (OpMult c c') = (eval c) * (eval c') | |
eval (OpDivd c c') = (eval c) / (eval c') -- divide by zero doesn't error - that's 'inifinity' | |
-- PARSING | |
-- <expression> ::= <term> <plussubt> | <term> | |
-- <plussubt> ::= + <expression> | - <expression> | |
-- <term> ::= <factor> <multdivd> | <factor> | |
-- <multdivd> ::= * <factor> | / <factor> | |
-- <factor> ::= "(" <expression> ")" | number | |
-- Parsers | |
calc = expression | |
expression = term `chainl1` plusSubt <||> term | |
term = factor `chainl1` multDivd <||> factor | |
factor = parens expression <||> number | |
multDivd = withSpaces mult <||> withSpaces divd | |
plusSubt = withSpaces plus <||> withSpaces subt | |
-- minimal parsers | |
mult = char '*' >> return OpMult | |
plus = char '+' >> return OpPlus | |
subt = char '-' >> return OpSubt | |
divd = char '/' >> return OpDivd | |
number = Number <$> (read <$> many1 digit) | |
-- helpers | |
(<||>) :: Parser a -> Parser a -> Parser a | |
a <||> b = try a <|> try b | |
parens :: Parser a -> Parser a | |
parens = between (char '(') (char ')') | |
withSpaces :: Parser a -> Parser a | |
withSpaces = between spaces spaces | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment