blob: 23e15ff71f539516370c26e210f1359868ae3465 (
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
module buildclient.config.BuildParser where
import Data.MicroParsec as MP
data Token =
BareWord {bval::String}
| Number {nval::String}
| BraceOpen
| BraceClose
| NewLine
| QuotedString {sval::String}
| ERROR {position::Int, message:: String}
derive Eq Token
derive Show Token
protected type Parser = MP.Parser [] Token
protected isIdentifierStartChar :: Char -> Bool
protected isIdentifierStartChar char = ('A' <= char && char <= 'Z') || ('a' <= char && char <= 'z') || char == '_'
protected isDigit :: Char -> Bool
protected isDigit char = ('0' <= char && char <= '9')
protected isIdentifierChar :: Char -> Bool
protected isIdentifierChar char = isIdentifierStartChar char || isDigit char
lexer :: String -> [Token]
lexer s = lex s 0
private lex :: String -> Int -> [Token]
private lex !cs !start
| endOfSeq = []
| ch == '\\' && at 1 == '\n' = lex cs (start + 2)
| ch == '\n' = Token.NewLine : lex cs (start + 1)
| ch.isWhitespace = lex cs (start + 1)
| ch == '"' = string 1 []
| ch == '{' = Token.BraceOpen : lex cs (start + 1)
| ch == '}' = Token.BraceClose : lex cs (start + 1)
| isDigit ch = takeWhile isDigit Token.Number 0 []
| isIdentifierStartChar ch = takeWhile isIdentifierChar Token.BareWord 0 []
| otherwise -> [Token.ERROR{position=start, message="Unexpected character"}]
where
endOfSeq = start >= cs.length
ch = at 0
at i = if start + i >= cs.length then '\0' else cs.charAt (start + i)
takeWhile charTest mapper off !acc
| charTest $ at off = takeWhile charTest mapper (off + 1) (at off : acc)
| otherwise = (mapper $ packed $ reverse acc) : lex cs (start + off)
string off !acc
| start + off >= cs.length = [Token.ERROR{position=start + off, message="Unterminated string literal"}]
| ch == '"' -> (Token.QuotedString $ packed (reverse acc)) : lex cs (start + off + 1)
| ch >= ' ' || ch == '\n' || ch == '\t' -> string (off + 1) (ch : acc)
| otherwise -> [Token.ERROR{position=start+off, message="Unknown string character"}]
where
ch = at off
runParser :: Parser a -> [Token] -> (String | a)
runParser p ts = case MP.runid p ts of
(Left msg, ts) → Left (MP.reporterror ts msg)
(right, _) → right
protected parseNewLine :: Parser ()
protected parseNewLine = const () <$> expect Token.NewLine <?> "newline expected"
protected parseLBrace :: Parser ()
protected parseLBrace = const () <$> expect Token.BraceOpen <?> "opening braces expected"
protected parseRBrace :: Parser ()
protected parseRBrace = const () <$> expect Token.BraceClose <?> "closing braces expected"
protected parseEndOfStatement :: Parser ()
protected parseEndOfStatement = (parseNewLine <|> parseRBrace <|> eos) <?> "expected end of statement"
protected parseBareWord :: Parser String
protected parseBareWord = (_.bval) <$> satisfy _.{bval?} <?> "bareword expected"
protected parseQuotedString :: Parser String
protected parseQuotedString = (_.sval) <$> satisfy _.{sval?} <?> "string expected"
protected parseNumber :: Parser String
protected parseNumber = (_.nval) <$> satisfy _.{nval?} <?> "number expected"
data ASTValue = ASTString String | ASTWord String | ASTNumber Int
data ASTDirective = ASTDirective {name::String, arguments:: [ASTValue], block:: [ASTDirective]}
derive Show ASTValue
derive Show ASTDirective
protected parseValue :: Parser ASTValue
protected parseValue = ASTString <$> parseQuotedString <|> ASTWord <$> parseBareWord <|> (ASTNumber . read) <$> parseNumber
protected parseShortDirective :: Parser ASTDirective
protected parseShortDirective = do
skip parseNewLine
name <- parseBareWord <?> "directive name expected"
args <- many parseValue
cond (parseEndOfStatement)
(return $ ASTDirective name args [])
do
parseLBrace
subdirectives <- many parseShortDirective
skip parseNewLine
parseRBrace
return $ ASTDirective name args subdirectives
parseFile :: Parser [ASTDirective]
parseFile = (many parseShortDirective) <* (skip parseNewLine) <* eos
findDirective :: String -> [ASTDirective] -> Maybe ASTDirective
findDirective name (directive:ds)
| directive.name == name = Just directive
| otherwise = findDirective name ds
findDirective _ [] = Nothing
|