blob: 7b6482224f2b8c39a224a40bf13beb8631a26920 (
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
import Scanner.Protocol
import Scanner.Xml.Basic
import Scanner.Xml.Parser
/-!
# Parsing of Protocol Description
This converts a protocol description XML into a `Raw.ProtocolDesc`.
-/
private def String.toNat (s : String) : Except String Nat :=
match Lean.Syntax.decodeNatLitVal? s with
| .some n => .ok n
| .none => .error s!"Expected a natural number, but got {s}"
private def String.toBool : String → Except String Bool
| "true" => .ok true
| "false" => .ok false
| s => .error s!"Expected a boolean value, but got {s}"
namespace Lean.Xml
private def Element.name : Xml.Element → String
| .Element name _ _ => name
private def Element.attributes : Xml.Element → Xml.Attributes
| .Element _ attrs _ => attrs
private def Element.content : Xml.Element → Array Xml.Content
| .Element _ _ content => content
private def Element.ensureName (el : Xml.Element) (name : String) : Except String Unit :=
if name == el.name
then .ok ()
else .error s!"Invalid tag: Expected {name}, but got {el.name}"
private def Element.textContent (el : Xml.Element) : String :=
let reformat (str : String) : String :=
str
|>.split "\n"
|>.map (String.Slice.trimAscii)
|>.map (String.Slice.toString)
|>.toList
|> String.intercalate "\n"
|>.trimAscii
|>.toString
reformat <| String.join <| Array.toList <| el.content.flatMap fun
| .Element _ => #[]
| .Comment _ => #[]
| .Character s => #[s]
private def Element.getChildrenByName (el : Xml.Element) (name : String) : Array Xml.Element :=
el.content.flatMap fun
| .Element child => if child.name == name then #[child] else #[]
| .Comment _ => #[]
| .Character _ => #[]
private def Element.getChildByName (el : Xml.Element) (name : String) : Option Xml.Element :=
match el.getChildrenByName name with
| #[single] => .some single
| _ => .none
private def Element.getAttribute (el : Xml.Element) (attr : String) : Option String :=
el.attributes.get? attr
private def Element.getRequiredAttribute (el : Xml.Element) (attr : String) : Except String String :=
el.getAttribute attr
|>.map pure
|>.getD (.error s!"Missing required attribute {attr}")
end Lean.Xml
open Lean
namespace WaylandScanner
open Raw
private def MessageType.ofString : String → Except String MessageType
| "destructor" => .ok .destructor
| s => .error s!"Expected \"destructor\", but got {s}"
private def ArgumentType.ofString : String → Except String ArgumentType
| "int" => .ok .int
| "uint" => .ok .uint
| "fixed" => .ok .fixed
| "string" => .ok .string
| "array" => .ok .array
| "fd" => .ok .fd
| "new_id" => .ok .newID
| "object" => .ok .object
| s => .error s!"Expected an argument type, but got {s}"
private def processDescription (el : Xml.Element) : Except String Description := do
el.ensureName "description"
let body := el.textContent
return {
summary := el.getAttribute "summary"
body := if body.isEmpty then .none else .some body
}
private def processEnumEntry (el : Xml.Element) : Except String EnumEntryDesc := do
el.ensureName "entry"
let name ← el.getRequiredAttribute "name"
let value ← (← el.getRequiredAttribute "value")
|>.toNat
let description ← el.getChildByName "description"
|>.mapM processDescription
let summary := el.getAttribute "summary"
let since ← el.getAttribute "since"
|>.mapM String.toNat
let deprecatedSince ← el.getAttribute "deprecated-since"
|>.mapM String.toNat
return {
name := name
value := value
description := description <|> summary.map Description.ofSummary
since := since.getD 1
deprecatedSince := deprecatedSince
}
private def processEnum (el : Xml.Element) : Except String EnumDesc := do
el.ensureName "enum"
let name ← el.getRequiredAttribute "name"
let description ← el.getChildByName "description"
|>.mapM processDescription
let since ← el.getAttribute "since"
|>.mapM String.toNat
let bitfield ← el.getAttribute "bitfield"
|>.mapM String.toBool
let entries ← el.getChildrenByName "entry"
|>.mapM processEnumEntry
return {
name := name
description := description
since := since.getD 1
bitfield := bitfield.getD false
entries := entries
}
private def processArgument (el : Xml.Element) : Except String ArgumentDesc := do
el.ensureName "arg"
let name ← el.getRequiredAttribute "name"
let type ← (← el.getRequiredAttribute "type")
|> ArgumentType.ofString
let description ← el.getChildByName "description"
|>.mapM processDescription
let summary := el.getAttribute "summary"
let interface := el.getAttribute "interface"
let enum := el.getAttribute "enum"
let allowNull ← el.getAttribute "allow-null"
|>.mapM String.toBool
return {
name := name
type := type
description := description <|> summary.map Description.ofSummary
interface := interface
enum := enum
allowNull := allowNull.getD false
}
private def processMessage (opcode : Nat) (el : Xml.Element) : Except String MessageDesc := do
let name ← el.getRequiredAttribute "name"
let description ← el.getChildByName "description"
|>.mapM processDescription
let type ← el.getAttribute "type"
|>.mapM MessageType.ofString
let since ← el.getAttribute "since"
|>.mapM String.toNat
let deprecatedSince ← el.getAttribute "deprecated-since"
|>.mapM String.toNat
let args ← el.getChildrenByName "arg"
|>.mapM processArgument
return {
name := name
opcode := opcode
description := description
type := type.getD .normal
since := since.getD 1
deprecatedSince := deprecatedSince
arguments := args
}
private def processInterface (el : Xml.Element) : Except String InterfaceDesc := do
el.ensureName "interface"
let name ← el.getRequiredAttribute "name"
let version ← (← el.getRequiredAttribute "version")
|>.toNat
let description ← el.getChildByName "description"
|>.mapM processDescription
let enums ← el.getChildrenByName "enum"
|>.mapM processEnum
let requests ← el.getChildrenByName "request"
|>.mapIdxM processMessage
let events ← el.getChildrenByName "event"
|>.mapIdxM processMessage
return {
name := name
version := version
description := description
enums := enums
requests := requests
events := events
}
private def processProtocol (el : Xml.Element) : Except String ProtocolDesc := do
el.ensureName "protocol"
let name ← el.getRequiredAttribute "name"
let copyright := el.getChildByName "copyright"
|>.map Xml.Element.textContent
let description ← el.getChildByName "description"
|>.mapM processDescription
let interfaces ← el.getChildrenByName "interface"
|>.mapM processInterface
return {
name := name
copyright := copyright
description := description
interfaces := interfaces
}
def parse (text : String) : Except String ProtocolDesc :=
Xml.parse text >>= processProtocol
end WaylandScanner
|