Skip to content

Instantly share code, notes, and snippets.

@cblp
Last active October 23, 2019 12:09
Show Gist options
  • Save cblp/149a09e6e2c916cb632a0eba13238b0d to your computer and use it in GitHub Desktop.
Save cblp/149a09e6e2c916cb632a0eba13238b0d to your computer and use it in GitHub Desktop.
#!/usr/bin/env stack
{-
stack --resolver=lts-14.10
script --ghc-options=-Wall --package=megaparsec --package=parser-combinators
-}
import Control.Monad.Combinators ((<|>), between, some)
import Control.Monad.Combinators.Expr (Operator (InfixL), makeExprParser)
import Data.Void (Void)
import Text.Megaparsec (Parsec, eof, errorBundlePretty, parse)
import Text.Megaparsec.Char (letterChar, string)
import Text.Megaparsec.Char.Lexer (decimal)
main :: IO ()
main = do
let f = eval "(a*b+(c*d)/2)*l"
print (f [("a", 3), ("b", 15), ("c", 9), ("d", 20), ("l", 42)])
eval :: String -> [(String, Integer)] -> Integer
eval s args = case parse (expr args <* eof) "expression" s of
Right r -> r
Left err -> error (errorBundlePretty err)
expr :: [(String, Integer)] -> Parsec Void String Integer
expr args =
makeExprParser
term
[[binary "*" (*), binary "/" div], [binary "+" (+), binary "-" (-)]]
where
binary name f = InfixL (f <$ string name)
term = var <|> decimal <|> between (string "(") (string ")") (expr args)
var = do
varName <- some letterChar
case lookup varName args of
Just n -> pure n
Nothing -> fail ("no variable named " ++ varName ++ " in args")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment