summaryrefslogtreecommitdiff
path: root/src/SPL/Parse.hs
blob: 4331ccbf4a1fe0d86a9759043a186bb6828d6dbf (plain) (blame)
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
-- vim: et ts=2 sw=2 ai:
{-# LANGUAGE LambdaCase #-}
module SPL.Parse (parse)
where

import Control.Applicative
import Control.Monad
import Data.Functor

import Text.Parsec (sepBy, (<?>))
import qualified Text.Parsec as P

import SPL.Syntax hiding (TInt,TBool,TChar,TArrow)
import qualified SPL.Syntax
import SPL.Lex

type Parser t = P.Parsec [Token] () t

satisfy :: (Token -> Bool) -> Parser Token
satisfy p = P.tokenPrim
  show
  (const . const)
  (\t -> if p t then Just t else Nothing)

token :: Token -> Parser Token
token t = satisfy (== t) <?> show t

check :: (Token -> Maybe a) -> Parser a
check f = P.tokenPrim
  show
  (const . const)
  f

parse :: [Token] -> Either P.ParseError Program
parse = P.parse spl "Not a valid program" . filter (not . isCommentToken)

spl :: Parser Program
spl = collect <$> (many toplevel <* P.eof)
  where
    collect :: [Either Variable Function] -> Program
    collect vfs = Program [f | Right f <- vfs] [v | Left v <- vfs]

toplevel :: Parser (Either Variable Function)
toplevel = Left <$> var <|> Right <$> fun

comment :: Parser String
comment = toString <$> satisfy isCommentToken
  where
    toString (TSingleComment s) = s
    toString (TBlockComment s)  = s

parenthesised :: Parser a -> Parser a
parenthesised p = token TParenOpen *> p <* token TParenClose

braced :: Parser a -> Parser a
braced p = token TBraceOpen *> p <* token TBraceClose

var :: Parser Variable
var = do
  t  <- Just <$> plainType <|> (token TVar $> Nothing)
  id <- ident
  token TEquals
  val <- expr
  token TSemicolon
  return $ Variable id t val

fun :: Parser Function
fun = do
  id <- ident
  args <- parenthesised $ ident `sepBy` token TComma
  ftype <- optional (token TColonColon *> funType)
  token TBraceOpen
  vars <- many var
  stmt <- statements
  token TBraceClose
  return $ Function id ftype args vars stmt

ident :: Parser Name
ident = (\(TIdent id) -> id) <$> satisfy isIdentToken

funType :: Parser Type
funType = do
  argtypes <- many plainType
  token TArrow
  rettype <- plainType
  return $ SPL.Syntax.TArrow argtypes rettype

plainType :: Parser Type
plainType = -- TODO
  token TIntType  $> SPL.Syntax.TInt  <|>
  token TBoolType $> SPL.Syntax.TBool <|>
  token TCharType $> SPL.Syntax.TChar

expr :: Parser Expression
expr = literal -- TODO
  where
    literal :: Parser Expression
    literal = fmap Literal $ check $ \case
      TInt i  -> Just (LInt i)
      TBool b -> Just (LBool b)
      _       -> Nothing

statements :: Parser Statement
statements = foldr1 Seq <$> some statement <|> pure Nop

statement :: Parser Statement
statement =
  liftM3 If
    (token TIf *> parenthesised expr)
    (braced statements)
    (optional (token TElse *> braced statements)) <|>
  liftM2 While
    (token TWhile *> parenthesised expr)
    (braced statements) <|>
  liftM2 Assign
    ident
    (token TEquals *> expr <* token TSemicolon) <|>
  Eval <$> (expr <* token TSemicolon) <|>
  Return <$> (token TReturn *> optional expr <* token TSemicolon)