blob: 88853a6943552a35393b8bc11956a57995ff4c3c (
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
|
import Scanner.Protocol
import Std.Data.TreeMap
namespace WaylandScanner
private structure InterfaceInfo where
moduleName : Identifier
enums : Array Identifier
private abbrev InterfaceMap := Std.TreeMap String InterfaceInfo
private structure Context where
interfaces : InterfaceMap
currentInterface : Client.InterfaceRef
referencedInterfaces : Array Client.InterfaceRef
private abbrev Processor : Type → Type := StateT Context (Except String)
private def processEnumEntry (raw : Raw.EnumEntryDesc) : Processor Client.EnumEntryDesc := do
return {
name := raw.name
value := raw.value
description := raw.description
since := raw.since
deprecatedSince := raw.deprecatedSince
}
private def interfaceInfo (name : String) : Processor InterfaceInfo := do
match (← get).interfaces.get? name with
| .some info => pure info
| .none => .error s!"Unable to find referenced interface {name}"
private def resolveInterface (name : String) : Processor Client.InterfaceRef := do
let info ← interfaceInfo name
let ref := {
moduleName := info.moduleName
typeName := name
}
let old ← get
set { old with referencedInterfaces := old.referencedInterfaces ++ #[ref] }
return ref
private def resolveEnum (name : String) : Processor Client.EnumRef := do
let (interface, enum) ←
match (name.split ".").toArray with
| #[enum] => pure ((← get).currentInterface.typeName, enum.toString)
| #[interface, enum] => pure (interface.toString, enum.toString)
| _ => throw s!"Malformed enum reference {name}"
let info ← interfaceInfo interface
if not (info.enums.contains enum) then
throw s!"Interface {repr interface} does not contain enum {repr enum}"
return {
moduleName := info.moduleName
typeName := interface
enumName := enum
}
private def processEnum (raw : Raw.EnumDesc) : Processor Client.EnumDesc := do
return {
name := raw.name
description := raw.description
since := raw.since
bitfield := raw.bitfield
entries := ← raw.entries.mapM processEnumEntry
}
private def processArgument (raw : Raw.ArgumentDesc) : Processor Client.ArgumentDesc := do
return {
name := raw.name
type :=
← match raw.type with
| .int => Client.ArgumentType.int <$> raw.enum.mapM resolveEnum
| .uInt => Client.ArgumentType.uInt <$> raw.enum.mapM resolveEnum
| .fixed => Client.ArgumentType.fixed |> pure
| .string => Client.ArgumentType.string (.ofAllowNull raw.allowNull) |> pure
| .array => Client.ArgumentType.array |> pure
| .fd => Client.ArgumentType.fd |> pure
| .newID => Client.ArgumentType.object .notNull <$> raw.interface.mapM resolveInterface
| .object => Client.ArgumentType.object (.ofAllowNull raw.allowNull) <$> raw.interface.mapM resolveInterface
description := raw.description
}
private def processRequest (raw : Raw.MessageDesc) : Processor Client.RequestDesc := do
let newIDArgs := raw.arguments.filter fun arg ↦ arg.type == .newID
let otherArgs := raw.arguments.filter fun arg ↦ arg.type != .newID
let returnType ←
match newIDArgs with
| #[] => Client.ReturnType.unit |> pure
| #[arg] => Client.ReturnType.object <$> arg.interface.mapM resolveInterface
| _ => throw s!"Expected at most one new_id argument for request in {raw.name}"
return {
name := raw.name
opcode := raw.opcode
description := raw.description
isDestructor := raw.type == .destructor
since := raw.since
deprecatedSince := raw.deprecatedSince
arguments := ← otherArgs.mapM processArgument
returnType := returnType
}
private def processEvent (raw : Raw.MessageDesc) : Processor Client.EventDesc := do
return {
name := raw.name
opcode := raw.opcode
description := raw.description
isDestructor := raw.type == .destructor
since := raw.since
deprecatedSince := raw.deprecatedSince
arguments := ← raw.arguments.mapM processArgument
}
private def processInterface (raw : Raw.InterfaceDesc) : Processor Client.InterfaceDesc := do
let requests ← raw.requests.mapM processRequest
let events ← raw.events.mapM processEvent
let state ← get
let referencedInterfaces := state.referencedInterfaces ++ #[state.currentInterface]
return {
name := raw.name
version := raw.version
description := raw.description
enums := ← raw.enums.mapM processEnum
requests := requests
events := events
referencedInterfaces := referencedInterfaces
|>.toList
|>.eraseDups
|>.toArray
}
private def processProtocol (interfaces : InterfaceMap) (raw : Raw.ProtocolDesc) : Except String Client.ProtocolDesc := do
return {
name := raw.name
copyright := raw.copyright
description := raw.description
interfaces := ← raw.interfaces.mapM
fun iface ↦ (processInterface iface).run' {
interfaces := interfaces
currentInterface := { moduleName := raw.name, typeName := iface.name }
referencedInterfaces := #[]
}
}
def processProtocols (raw : Array Raw.ProtocolDesc) : Except String (Array Client.ProtocolDesc) :=
let interfaces : InterfaceMap := .ofArray <| raw.flatMap
fun protocol ↦ protocol.interfaces.map
fun iface ↦ (iface.name, (⟨protocol.name, iface.enums.map Raw.EnumDesc.name⟩ : InterfaceInfo))
raw.mapM (processProtocol interfaces)
end WaylandScanner
|