summaryrefslogtreecommitdiff
path: root/parser.hs
blob: 4f9e965cb173353399838d5f1c74a99921649d2e (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
module Parser(parseProgram, parseExpression) where

import Control.Monad
import Data.Char
import Text.Parsec

import AST


type Parser = Parsec String ()


parseProgram :: String -> Either ParseError Program
parseProgram = parse pProgram ""

pProgram :: Parser Program
pProgram = between pWhiteComment eof (liftM Program (many pValue))


parseExpression :: String -> Either ParseError Value
parseExpression = parse pExpression ""

pExpression :: Parser Value
pExpression = between pWhiteComment eof pValue


pValue :: Parser Value
pValue = pVEllipsis <|> pVList <|> pVNum <|> pVString <|> pVName <|> pVQuoted  <?> "value"

pVList :: Parser Value
pVList = flip label "list" $ do
    symbol "("
    exs <- many pValue
    symbol ")"
    return $ VList exs

pVNum :: Parser Value
pVNum = liftM (VNum . read) (many1 digit) <* pWhiteComment  <?> "number"

pVString :: Parser Value
pVString = flip label "string" $ do
    void $ char '"'
    s <- manyTill anyChar (symbol "\"")
    return $ VString s

pVName :: Parser Value
pVName = flip label "name" $ do
    first <- satisfy isFirstNameChar
    rest <- many (satisfy isNameChar)
    pWhiteComment
    return $ VName $ first : rest
  where
    isNameChar c = ((c >= '*' && c <= 'z') || c `elem` "!#$%&|~") && c /= ';'
    isFirstNameChar c = isNameChar c && not (isDigit c)

pVQuoted :: Parser Value
pVQuoted = char '\'' >> liftM VQuoted pValue  <?> "quoted value"

pVEllipsis :: Parser Value
pVEllipsis = symbol "..." >> return VEllipsis  <?> "ellipsis"


symbol :: String -> Parser ()
symbol s = try (string s) >> pWhiteComment

pWhiteComment :: Parser ()
pWhiteComment = do
    pWhitespace
    void $ many $ pComment >> pWhitespace
  where
    pWhitespace :: Parser ()
    pWhitespace = void (many space)  <?> "whitespace"

    pComment :: Parser ()
    pComment = flip label "comment" $ do
        void $ char ';'
        void (manyTill anyChar (void endOfLine <|> eof))