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

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

import AST


type Parser = ParsecT String () IO


parseProgram :: String -> IO (Either ParseError Program)
parseProgram = runParserT pProgram () ""

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


parseExpression :: String -> IO (Either ParseError Value)
parseExpression = runParserT pExpression () ""

pExpression :: Parser Value
pExpression = between pWhiteComment eof pValue


pValue :: Parser Value
pValue = pPPC <|> 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 = fmap VString pString

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"

pPPC :: Parser Value
pPPC = flip label "preprocessor command" $ do
    symbol "#include"
    fname <- pString
    src <- liftIO $ readFile fname
    stateBackup <- getParserState
    void $ setParserState (State ("(do " ++ src ++ ")") (initialPos fname) ())
    result <- pValue <* eof
    void $ setParserState stateBackup
    return result


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

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

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))