-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathLexer.hs
108 lines (94 loc) · 2.3 KB
/
Lexer.hs
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
module Lexer where
import Prelude (read)
import Protolude hiding (one, many, many1)
import Common.ParserT
import qualified Data.Text as T
import Lexer.Token
import Lexer.Types
import Utils (unsafeFromRight, isLetter, isDigit, (<||>))
lexToken :: Lexer Token
lexToken = choose
[ lexOperator
, lexPunctuation
, lexString
, lexReservedOrIdent
, lexInteger
, lexIllegal
]
parseMap :: Text -> Token -> Lexer Token
parseMap str tkn = string str $> tkn
lexOperator :: Lexer Token
lexOperator = choose
[ parseMap "==" Eq
, parseMap "=" Assign
, parseMap "+" Plus
, parseMap "-" Minus
, parseMap "*" Multiply
, parseMap "/" Divide
, parseMap "!=" NotEq
, parseMap "!" Not
, parseMap ">" GreaterThan
, parseMap "<" LessThan
]
lexPunctuation :: Lexer Token
lexPunctuation = choose
[ parseMap ":" Colon
, parseMap ";" SemiColon
, parseMap "," Comma
, parseMap "(" LParen
, parseMap ")" RParen
, parseMap "{" LBrace
, parseMap "}" RBrace
, parseMap "[" LBracket
, parseMap "]" RBracket
]
lexString :: Lexer Token
lexString = do
atom '"'
x <- go -- will lex the closing double quotation mark too
return $ StringLiteral x
where
go :: Lexer Text
go = do
c <- next
case c of
'"' -> return ""
'\\' -> do
c <- next
T.cons (case c of
'n' -> '\n'
't' -> '\t'
c -> c) <$> go
c -> T.cons c <$> go
letter :: Lexer Char
letter = predicate isLetter
digit :: Lexer Char
digit = predicate isDigit
lexReservedOrIdent :: Lexer Token
lexReservedOrIdent = do
str <- (:) <$> letter <*> many (letter <|> digit)
return $ case str of
"let" -> Let
"fn" -> Function
"if" -> If
"else" -> Else
"return" -> Return
"true" -> BoolLiteral True
"false" -> BoolLiteral False
_ -> Ident (T.pack str)
lexInteger :: Lexer Token
lexInteger = IntLiteral . read <$> many1 digit
lexIllegal :: Lexer Token
lexIllegal = consume $> Illegal
skipWhitespaces :: Lexer ()
skipWhitespaces = many (predicate $ flip elem [' ', '\t', '\n', '\r']) >> return ()
lex :: Text -> Either ParserError [Token]
lex = execLexer go
where
go :: Lexer [Token]
go = do
skipWhitespaces
c <- preview
case c of
Nothing -> return [EOF]
Just x -> (:) <$> lexToken <*> go