module Language.Eval (expr, eval) where
import Data.Text
import Text.Parsec.Text
import Text.Parsec.Prim
import Text.Parsec.Expr
import Text.Parsec.Token
import Text.Parsec.Char (satisfy, alphaNum, char, oneOf, letter)
import Text.Parsec.Language (emptyDef, javaStyle)
import Data.Functor.Identity
import Data.Bits
ezLanguage :: GenLanguageDef Text [Integer] Identity
ezLanguage = LanguageDef {
caseSensitive = True
, commentStart = "/*"
, commentEnd = "*/"
, commentLine = "#"
, nestedComments = False
, identStart = letter
, identLetter = letter
, opStart = oneOf "<>+-&|"
, opLetter = oneOf "<>"
, reservedNames = names
, reservedOpNames = ops
}
where
names = []
ops = ["<<",">>", "&", "|", "+", "-", "*"]
lexer = makeTokenParser ezLanguage
expr :: Stream Text Identity Char => ParsecT Text [Integer] Identity Integer
expr = buildExpressionParser table term
<?> "expression"
substituteSymbol = do
s <- lexeme lexer letter <?> "variable"
let idx = fromEnum s fromEnum 'a'
u <- getState
return $ u !! idx
term = parens lexer expr
<|> integer lexer
<|> substituteSymbol
<?> "simple expression"
shiftr :: Bits a => a -> Integer -> a
shiftr x b = shiftR x (fromIntegral b)
shiftl :: Bits a => a -> Integer -> a
shiftl x b = shiftL x (fromIntegral b)
table = [ [prefix "-" negate, prefix "+" id ]
, [postfix "++" (+1)]
, [binary "<<" shiftl AssocLeft, binary ">>" shiftr AssocLeft ]
, [binary "*" (*) AssocLeft, binary "/" div AssocLeft, binary "&" (.&.) AssocLeft ]
, [binary "+" (+) AssocLeft, binary "-" () AssocLeft ]
, [binary "|" (.|.) AssocLeft ]
]
binary name fun = Infix (do{ reservedOp lexer name; return fun })
prefix name fun = Prefix (do{ reservedOp lexer name; return fun })
postfix name fun = Postfix (do{ reservedOp lexer name; return fun })
test = runParser expr [1, 2] "" "(0xff & 0xf) << (a + b)"
eval :: Text -> [Integer] -> Integer
eval s vars = case runParser expr vars "" s of
Left e -> error $ show e
Right i -> i