module Main where import Control.Monad import Text.ParserCombinators.Parsec hiding (spaces) import System.Environment import Numeric data LispVal = Atom String | List [LispVal] | DottedList [LispVal] LispVal | Number Integer | String String | Character Char | Bool Bool deriving Show symbol :: Parser Char symbol = oneOf "!#$%&|*+-/:<=>?@^_~" spaces :: Parser () spaces = skipMany space nonPrintableChar :: Parser Char nonPrintableChar = do c <- char '\\' >> oneOf "\\nrtf" return $ case c of '\\' -> '\\' 'n' -> '\n' 'r' -> '\r' 't' -> '\t' 'f' -> '\f' parseCharacter :: Parser LispVal parseCharacter = do char '\'' c <- noneOf ['\\', '\''] <|> try singleQuote <|> try nonPrintableChar char '\'' return $ Character c where singleQuote = char '\\' >> char '\'' parseString :: Parser LispVal parseString = do char '"' x <- many innerChar char '"' return $ String x where innerChar = noneOf ['\\', '"'] <|> try doubleQuote <|> try nonPrintableChar doubleQuote = char '\\' >> char '"' parseAtom :: Parser LispVal parseAtom = do a <- letter <|> symbol b <- many (letter <|> digit <|> symbol) let atom = a:b return $ case atom of "#t" -> Bool True "#f" -> Bool False _ -> Atom atom parseNumber :: Parser LispVal parseNumber = do toNum <- radix ds <- many1 digit let ((a,_):_) = toNum ds return $ Number a where radix = do r <- try (char '#' >> oneOf "bodx") <|> return 'd' return $ case r of 'd' -> readDec 'x' -> readHex 'o' -> readOct 'b' -> readInt 2 (\x -> elem x "01") (read . (:[])) parseList :: Parser LispVal parseList = liftM List $ sepBy parseExpr spaces parseDottedList :: Parser LispVal parseDottedList = do head <- endBy parseExpr spaces tail <- char '.' >> spaces >> parseExpr return $ DottedList head tail parseQuoted :: Parser LispVal parseQuoted = do char '\'' x <- parseExpr return $ List [Atom "quote", x] parseExpr :: Parser LispVal parseExpr = parseString <|> parseNumber <|> parseAtom <|> try parseCharacter <|> parseQuoted <|> do char '(' x <- try parseList <|> parseDottedList char ')' return x readExpr :: String -> String readExpr input = case parse parseExpr "lisp" input of Left err -> "No match: " ++ show err Right val -> "Found value: " ++ showVal val showVal :: LispVal -> String showVal (Atom atom) = atom showVal (List list) = "(" ++ unwordsList list ++ ")" showVal (DottedList head tail) = "(" ++ unwordsList head ++ " . " ++ showVal tail ++ ")" showVal (Number n) = show n showVal (String str) = "\"" ++ str ++ "\"" showVal (Character c) = "'" ++ [c] ++ "'" showVal (Bool True) = "#t" showVal (Bool False) = "#f" unwordsList :: [LispVal] -> String unwordsList = unwords . map showVal main :: IO () main = do args <- getLine putStrLn (readExpr args) main