-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParser.hs
108 lines (92 loc) · 3.57 KB
/
Parser.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
-- |
-- This module parses a Jago document to an HTML representation
-- defined in @Jago.AST@
module Jago.Parser
( parse
) where
import Control.Monad ( void )
import qualified Data.Text as T
import Jago.AST ( Attribute
, Document
, Html(Element, Text)
)
import Text.Parsec ( (<?>)
, (<|>)
, ParseError
, alphaNum
, anyChar
, between
, char
, choice
, eof
, many
, many1
, manyTill
, oneOf
, option
, sepBy
, try
)
import Text.Parsec.Indent ( IndentParser
, indented
, runIndentParser
, withPos
)
-- | Indentation-sensitive parser for Jago
type Parser a = IndentParser T.Text () a
-- | Whitespace and comment definition
sc :: Parser ()
sc = choice [whitespace *> sc, comment *> sc, return ()]
where
comment =
try (char '#') *> manyTill anyChar (void (char '\n') <|> eof) <?> "comment"
whitespace = void (many1 (oneOf " \t\n") <?> "whitespace")
-- | Lexeme definition
lexeme :: Parser a -> Parser a
lexeme p = p <* sc
-- | Symbol helper
symbol :: Char -> Parser ()
symbol = void . lexeme . char
-- | Identifier definition
identifier :: Parser T.Text
identifier = do
id <- many1 ch
return $ T.pack id
where ch = alphaNum <|> char '-'
-- | String definition
string :: Parser T.Text
string = string' (char '"') <|> string' (char '`') <?> "string"
where
string' :: Parser Char -> Parser T.Text
string' g = do
str <- g *> manyTill anyChar (lexeme g)
return $ T.pack str
-- | Attribute definition
attribute :: Parser Attribute
attribute = do
k <- identifier <* symbol '=' <?> "attribute name"
v <- string <?> "attribute value"
return (k, v) <?> "attribute"
-- | Attributes definition
attributes :: Parser [Attribute]
attributes =
between (symbol '[') (symbol ']') (sepBy attribute sc) <?> "attributes"
-- | Literal definition
literal :: Parser Html
literal = lexeme (Text <$> string) <?> "literal text"
-- | Tag definition
element :: Parser Html
element = withPos $ do
name <- lexeme identifier <?> "tag name"
attrs <- lexeme $ option [] attributes
child <- lexeme $ many $ indented *> html
return (Element name attrs child) <?> "tag"
-- | Html definition
html :: Parser Html
html = lexeme $ element <|> literal
-- | Document definition
document :: Parser Document
document = sc *> many html <* eof
-- | Parse jago document to AST representation defined in @Jago.AST@
parse :: FilePath -> T.Text -> Either ParseError Document
parse = runIndentParser document ()