This repository was archived by the owner on Aug 17, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParser.fs
More file actions
122 lines (85 loc) · 3.34 KB
/
Parser.fs
File metadata and controls
122 lines (85 loc) · 3.34 KB
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
120
121
122
//
// Parser for the KeyValues file format. github.com/quiverteam/qpc-parser
//
module KeyValues.Parser
open FParsec
open System.Collections.Generic
let test parser str =
match run parser str with
| Success(res, _, _) -> printfn "%A" res
| Failure(msg, _, _) -> printfn "%s" msg
// A condition that must be met for something, e.g:
//
// $Include "foo" [$VAR]
//
// foo is included if $VAR is defined. Useful for system specific declarations
type Condition = AndCond of (Condition * Condition)
// One or more of the conditions is true
| OrCond of (Condition * Condition)
// Some condition is false
| NotCond of Condition
// Some var is defined
| DefCond of string
// Always true, used if empty or no conditions provided.
| EmptyCond
type Value = StringValue of string
| BlockValue of Syntax list
and Syntax = Statement of (string * Value * Condition)
let keyValues, keyValuesRef = createParserForwardedToRef<Syntax, unit>()
let str_ws s = pstring s >>. spaces
// Newlines **must** end in an NL char, an arbitrary number of CRs is allowed
// before, but there must be an NL at the end. Might break support on very,
// very old macs, but I'm not worried about that.
// let nlPadded = many (anyOf " \t\r") .>> pstring "\n"
let variable = spaces >>. regex "([\$a-zA-Z0-9_\-]+)" .>> spaces
let stringlit =
spaces >>. between (pstring "\"") (pstring "\"")
(manyChars (noneOf "\"\r\n"))
let block = spaces >>. pstring "{" >>. spaces >>. many keyValues .>> spaces .>> pstring "}" |>> BlockValue
// eat the spaces here so that the parser doesnt have to backtrack after
// variable matches whitespace
let keyString = spaces >>. (variable <|> stringlit)
let key = keyString |>> StringValue
let value = spaces >>. (key <|> block)
// Condition parsers. They parse conditions in square brackets. e.g:
// [$foo && $bar || $baz]
let opp = new OperatorPrecedenceParser<Condition, unit, unit>()
let cond = opp.ExpressionParser
let varCond = keyString |>> DefCond
let term = attempt varCond <|> between (str_ws "(") (str_ws ")") cond
opp.TermParser <- term
type Asc = Associativity
let addInfix a T =
opp.AddOperator(InfixOperator(a, spaces, 1, Asc.Left, fun x y -> T(x, y)))
addInfix "&&" AndCond
addInfix "||" OrCond
let condition = spaces >>. between (pstring "[") (pstring "]") cond
// Comments
let lineComment = pstring "//" >>. restOfLine true
let blockComment =
between
(pstring "/*")
(pstring "*/")
(charsTillString "*/" false System.Int32.MaxValue)
let comment = lineComment <|> blockComment |> skipMany
// Syntax rules
let conditionalStatement =
keyString .>>. value .>>.? condition
|> attempt
|>> fun ((x, y), z) -> Statement(x, y, z)
let unconditionalStatement =
keyString .>>. value
|>> fun (x, y) -> Statement(x, y, EmptyCond)
let statement =
conditionalStatement
<|> unconditionalStatement
do keyValuesRef := statement .>> spaces
let testVar s = test variable s
let testStatement s = test statement s
let testString s = test stringlit s
let testComment s = test comment s
let testCond s = test condition s
let testKey s = test key s
let testBlock s = test block s
// le chad actuall KeyValues parser
let parseKeyValues s = run keyValues s