aboutsummaryrefslogtreecommitdiff
path: root/src/Parser.hs
blob: dcbfdb1ad96c2a3fa1c07f2f1fef5d16b94a7a39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
module Parser ( parseLispValue
              , Expr(..)
              , parseString
              , parseInt
              , parseFloat
              , parseAtom
              , parseList
              , parseQuote
              , parseDottedList
              ) where

import           Control.Applicative           ((<$>))
import           Control.Monad                 (liftM)
import           Text.ParserCombinators.Parsec


type Ident = String

data Expr = List [Expr]
          | DottedList [Expr] Expr
          | StringLiteral String
          | IntLiteral Integer
          | FloatLiteral Double
          | BoolLiteral Bool
          | Id Ident
          deriving (Eq)

parseString :: Parser Expr
parseString = do
    char '"'
    innards <- many (noneOf "\"")
    char '"'
    return (StringLiteral innards)

parseInt :: Parser Expr
parseInt = IntLiteral . read <$> many1 digit

parseFloat :: Parser Expr
parseFloat = do
    characteristic <- many digit
    char '.'
    mantissa <- many1 digit
    return $ (FloatLiteral . read) $ characteristic ++ "." ++ mantissa

symbol :: Parser Char
symbol = oneOf "!#$%&|*+:/-=<?>@^_~"

parseAtom :: Parser Expr
parseAtom = do
    first <- letter <|> symbol
    rest <- many (letter <|> symbol <|> digit)
    let atom = first:rest
    return $ case atom of
               "#t" -> BoolLiteral True
               "#f" -> BoolLiteral False
               _    -> Id atom

whiteSpace :: Parser ()
whiteSpace = skipMany1 space

parseList :: Parser Expr
parseList = List <$> sepBy parseLispValue whiteSpace

parseDottedList :: Parser Expr
parseDottedList = do
    head <- endBy parseLispValue whiteSpace
    char '.'
    whiteSpace
    DottedList head <$> parseLispValue

parseQuote :: Parser Expr
parseQuote = do
    char '\''
    x <- parseLispValue
    return $ List [Id "quote", x]


parseLispValue :: Parser Expr
parseLispValue =
    try parseAtom
    <|> parseString
    <|> parseInt
    <|> parseQuote
    -- TODO: figure out a way to have floats and dotted lists
    -- <|> parseFloat
    <|> do
        char '('
        x <- try parseList <|> parseDottedList
        char ')'
        return x
    <?> "expected lisp value!"

instance Show Expr where
    show (DottedList xs x)   = "(" ++ unwords (map show xs) ++ " . " ++ show x ++ ")"
    show (List xs)           = "(" ++ unwords (map show xs) ++ ")"
    show (StringLiteral s)   = "\"" ++ s ++ "\""
    show (IntLiteral n)      = show n
    show (FloatLiteral n)    = show n
    show (BoolLiteral True)  = "#t"
    show (BoolLiteral False) = "#f"
    show (Id i) = i