hyprconf2lua 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hyprconf2lua/__init__.py +1 -0
- hyprconf2lua/__main__.py +4 -0
- hyprconf2lua/ast.py +171 -0
- hyprconf2lua/cli.py +173 -0
- hyprconf2lua/codegen.py +931 -0
- hyprconf2lua/converter.py +60 -0
- hyprconf2lua/lexer.py +74 -0
- hyprconf2lua/mappings.py +130 -0
- hyprconf2lua/parser.py +494 -0
- hyprconf2lua-1.2.0.dist-info/METADATA +242 -0
- hyprconf2lua-1.2.0.dist-info/RECORD +15 -0
- hyprconf2lua-1.2.0.dist-info/WHEEL +5 -0
- hyprconf2lua-1.2.0.dist-info/entry_points.txt +2 -0
- hyprconf2lua-1.2.0.dist-info/licenses/LICENSE +21 -0
- hyprconf2lua-1.2.0.dist-info/top_level.txt +1 -0
hyprconf2lua/parser.py
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from hyprconf2lua.lexer import Token, LexerError
|
|
5
|
+
from hyprconf2lua.ast import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
BIND_PREFIXES = {"bindm", "binde", "bindr", "bindl", "bindn", "bindo",
|
|
9
|
+
"bindt", "bindi", "bindp", "bindc", "bindd"}
|
|
10
|
+
|
|
11
|
+
BIND_FLAG_MAP = {
|
|
12
|
+
"bind": "",
|
|
13
|
+
"bindl": "l",
|
|
14
|
+
"bindr": "r",
|
|
15
|
+
"bindn": "n",
|
|
16
|
+
"bindo": "",
|
|
17
|
+
"bindm": "m",
|
|
18
|
+
"binde": "",
|
|
19
|
+
"bindt": "t",
|
|
20
|
+
"bindi": "i",
|
|
21
|
+
"bindp": "p",
|
|
22
|
+
"bindc": "c",
|
|
23
|
+
"bindd": "d",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _parse_combined_bind(directive: str) -> str:
|
|
28
|
+
flags = ""
|
|
29
|
+
if directive.startswith("bind"):
|
|
30
|
+
remaining = directive[4:]
|
|
31
|
+
for ch in remaining:
|
|
32
|
+
flags += BIND_FLAG_MAP.get("bind" + ch, "")
|
|
33
|
+
return flags
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ParserError(Exception):
|
|
37
|
+
def __init__(self, message: str, token: Token):
|
|
38
|
+
self.line = token.line
|
|
39
|
+
self.col = token.col
|
|
40
|
+
super().__init__(f"L{token.line}:{token.col}: {message}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Parser:
|
|
44
|
+
def __init__(self, tokens: List[Token]):
|
|
45
|
+
self.tokens = tokens
|
|
46
|
+
self.pos = 0
|
|
47
|
+
self.variables: Dict[str, str] = {}
|
|
48
|
+
|
|
49
|
+
def peek(self) -> Token:
|
|
50
|
+
return self.tokens[self.pos]
|
|
51
|
+
|
|
52
|
+
def advance(self) -> Token:
|
|
53
|
+
t = self.tokens[self.pos]
|
|
54
|
+
self.pos += 1
|
|
55
|
+
return t
|
|
56
|
+
|
|
57
|
+
def expect(self, type_: str) -> Token:
|
|
58
|
+
t = self.peek()
|
|
59
|
+
if t.type != type_:
|
|
60
|
+
raise ParserError(f"Expected {type_}, got {t.type} ({t.value!r})", t)
|
|
61
|
+
return self.advance()
|
|
62
|
+
|
|
63
|
+
def skip_newlines(self):
|
|
64
|
+
while self.peek().type == "NEWLINE":
|
|
65
|
+
self.advance()
|
|
66
|
+
|
|
67
|
+
def parse(self) -> ConfigFile:
|
|
68
|
+
body = self.parse_block(stop=None)
|
|
69
|
+
return ConfigFile(body)
|
|
70
|
+
|
|
71
|
+
def parse_block(self, stop: Optional[str] = None) -> Block:
|
|
72
|
+
stmts: Block = []
|
|
73
|
+
self.skip_newlines()
|
|
74
|
+
while self.peek().type != "EOF" and (stop is None or self.peek().type != stop):
|
|
75
|
+
stmt = self.parse_stmt()
|
|
76
|
+
if stmt is not None:
|
|
77
|
+
stmts.append(stmt)
|
|
78
|
+
self.skip_newlines()
|
|
79
|
+
return self._group_submaps(stmts)
|
|
80
|
+
|
|
81
|
+
def _group_submaps(self, stmts: Block) -> Block:
|
|
82
|
+
result: Block = []
|
|
83
|
+
i = 0
|
|
84
|
+
while i < len(stmts):
|
|
85
|
+
s = stmts[i]
|
|
86
|
+
if isinstance(s, Directive) and s.key == "submap" and s.value and s.value[0].strip().lower() != "reset":
|
|
87
|
+
name = s.value[0].strip()
|
|
88
|
+
body: Block = []
|
|
89
|
+
i += 1
|
|
90
|
+
while i < len(stmts):
|
|
91
|
+
inner = stmts[i]
|
|
92
|
+
if isinstance(inner, Directive) and inner.key == "submap":
|
|
93
|
+
val = inner.value[0].strip().lower() if inner.value else ""
|
|
94
|
+
if val == "reset":
|
|
95
|
+
i += 1
|
|
96
|
+
break
|
|
97
|
+
body.append(inner)
|
|
98
|
+
i += 1
|
|
99
|
+
result.append(SubmapDef(name, body, s.line))
|
|
100
|
+
elif isinstance(s, Directive) and s.key == "submap_reset":
|
|
101
|
+
result.append(Comment("# submap = reset (orphan, no matching submap start)", s.line))
|
|
102
|
+
i += 1
|
|
103
|
+
else:
|
|
104
|
+
result.append(s)
|
|
105
|
+
i += 1
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
def parse_stmt(self) -> Optional[BlockStmt]:
|
|
109
|
+
t = self.peek()
|
|
110
|
+
|
|
111
|
+
if t.type == "COMMENT":
|
|
112
|
+
return self.parse_comment()
|
|
113
|
+
if t.type == "BLOCK_CLOSE":
|
|
114
|
+
return None
|
|
115
|
+
if t.type == "DOLLAR":
|
|
116
|
+
return self.parse_variable_def()
|
|
117
|
+
if t.type == "IDENT":
|
|
118
|
+
return self.parse_ident_stmt()
|
|
119
|
+
if t.type == "NEWLINE":
|
|
120
|
+
self.advance()
|
|
121
|
+
return None
|
|
122
|
+
if t.type == "EOF":
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
raise ParserError(f"Unexpected token: {t.value!r}", t)
|
|
126
|
+
|
|
127
|
+
def parse_comment(self) -> Comment:
|
|
128
|
+
t = self.advance()
|
|
129
|
+
return Comment(t.value, t.line)
|
|
130
|
+
|
|
131
|
+
def parse_variable_def(self) -> VariableDef:
|
|
132
|
+
self.advance()
|
|
133
|
+
name_t = self.expect("IDENT")
|
|
134
|
+
self.expect("EQUALS")
|
|
135
|
+
val = self.parse_value_rest()
|
|
136
|
+
return VariableDef(name_t.value, val, name_t.line)
|
|
137
|
+
|
|
138
|
+
def parse_value_rest(self) -> str:
|
|
139
|
+
parts = []
|
|
140
|
+
while self.peek().type in ("IDENT", "STRING", "DOLLAR", "DOT", "COLON") or \
|
|
141
|
+
(self.peek().type == "EQUALS" and parts):
|
|
142
|
+
if self.peek().type == "COMMENT":
|
|
143
|
+
break
|
|
144
|
+
t = self.advance()
|
|
145
|
+
if t.type == "DOLLAR":
|
|
146
|
+
var_t = self.expect("IDENT")
|
|
147
|
+
parts.append("$" + var_t.value)
|
|
148
|
+
else:
|
|
149
|
+
parts.append(t.value)
|
|
150
|
+
if self.peek().type == "DOT":
|
|
151
|
+
self.advance()
|
|
152
|
+
parts.append(".")
|
|
153
|
+
|
|
154
|
+
return self._join_tokens(parts)
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def _join_tokens(tokens: List[str]) -> str:
|
|
158
|
+
if not tokens:
|
|
159
|
+
return ""
|
|
160
|
+
result = tokens[0]
|
|
161
|
+
for t in tokens[1:]:
|
|
162
|
+
no_space_before = {":", ",", "=", "+", "-", "%", "@", "^", "*", "|", "~"}
|
|
163
|
+
no_space_after = {":", ",", "="}
|
|
164
|
+
if t in no_space_before or result[-1:] in no_space_after:
|
|
165
|
+
result += t
|
|
166
|
+
else:
|
|
167
|
+
result += " " + t
|
|
168
|
+
return result.strip()
|
|
169
|
+
|
|
170
|
+
def parse_comma_values(self) -> List[str]:
|
|
171
|
+
values = []
|
|
172
|
+
current = []
|
|
173
|
+
while self.peek().type not in ("NEWLINE", "EOF") and \
|
|
174
|
+
self.peek().type != "COMMENT":
|
|
175
|
+
if self.peek().type == "COMMA":
|
|
176
|
+
self.advance()
|
|
177
|
+
values.append(self._join_tokens(current))
|
|
178
|
+
current = []
|
|
179
|
+
continue
|
|
180
|
+
if self.peek().type == "DOLLAR":
|
|
181
|
+
self.advance()
|
|
182
|
+
var_t = self.expect("IDENT")
|
|
183
|
+
current.append("$" + var_t.value)
|
|
184
|
+
else:
|
|
185
|
+
t = self.advance()
|
|
186
|
+
current.append(t.value)
|
|
187
|
+
remaining = self._join_tokens(current)
|
|
188
|
+
if remaining:
|
|
189
|
+
values.append(remaining)
|
|
190
|
+
return values
|
|
191
|
+
|
|
192
|
+
def resolve_var(self, val: str) -> str:
|
|
193
|
+
import re
|
|
194
|
+
def _repl(m: re.Match) -> str:
|
|
195
|
+
var_name = m.group(1)
|
|
196
|
+
if var_name in self.variables:
|
|
197
|
+
return self.variables[var_name]
|
|
198
|
+
return m.group(0)
|
|
199
|
+
return re.sub(r'\$(\w+)', _repl, val)
|
|
200
|
+
|
|
201
|
+
def parse_ident_stmt(self) -> BlockStmt:
|
|
202
|
+
t = self.advance()
|
|
203
|
+
directive = t.value
|
|
204
|
+
|
|
205
|
+
# Handle colon-separated nested keys: section:subsection:key
|
|
206
|
+
colon_parts = [directive]
|
|
207
|
+
while self.peek().type == "COLON":
|
|
208
|
+
self.advance()
|
|
209
|
+
if self.peek().type == "IDENT":
|
|
210
|
+
colon_parts.append(self.advance().value)
|
|
211
|
+
else:
|
|
212
|
+
break
|
|
213
|
+
directive = ":".join(colon_parts)
|
|
214
|
+
|
|
215
|
+
if directive.startswith("bind"):
|
|
216
|
+
if self.peek().type == "BLOCK_OPEN":
|
|
217
|
+
return self.parse_general_directive(directive, t.line)
|
|
218
|
+
return self.parse_bind(directive, t.line)
|
|
219
|
+
|
|
220
|
+
if directive == "monitor":
|
|
221
|
+
return self.parse_monitor(t.line)
|
|
222
|
+
if directive == "windowrule":
|
|
223
|
+
return self.parse_windowrule(False, t.line)
|
|
224
|
+
if directive == "windowrulev2":
|
|
225
|
+
return self.parse_windowrule(True, t.line)
|
|
226
|
+
if directive == "exec-once" or directive == "execr-once":
|
|
227
|
+
return self.parse_exec(directive, t.line)
|
|
228
|
+
if directive == "exec":
|
|
229
|
+
return self.parse_exec(directive, t.line)
|
|
230
|
+
if directive == "exec-shutdown":
|
|
231
|
+
return self.parse_exec(directive, t.line)
|
|
232
|
+
if directive == "animation":
|
|
233
|
+
return self.parse_animation(t.line)
|
|
234
|
+
if directive == "bezier":
|
|
235
|
+
return self.parse_bezier(t.line)
|
|
236
|
+
if directive == "env":
|
|
237
|
+
return self.parse_env(t.line)
|
|
238
|
+
if directive == "source":
|
|
239
|
+
return self.parse_source(t.line)
|
|
240
|
+
if directive == "gesture":
|
|
241
|
+
if self.peek().type == "BLOCK_OPEN":
|
|
242
|
+
return self.parse_gesture(t.line)
|
|
243
|
+
return self.parse_general_directive(directive, t.line)
|
|
244
|
+
if directive == "workspace":
|
|
245
|
+
return self.parse_workspace(t.line)
|
|
246
|
+
if directive == "layerrule":
|
|
247
|
+
return self.parse_layerrule(t.line)
|
|
248
|
+
if directive == "submap":
|
|
249
|
+
return self.parse_submap(t.line)
|
|
250
|
+
|
|
251
|
+
colon_idx = directive.find(":")
|
|
252
|
+
if colon_idx > 0:
|
|
253
|
+
prefix = directive[:colon_idx]
|
|
254
|
+
if prefix == "device":
|
|
255
|
+
return self.parse_device(directive[colon_idx + 1:], t.line)
|
|
256
|
+
|
|
257
|
+
# Handle colon-separated nested key: section:key = value
|
|
258
|
+
if len(colon_parts) >= 3:
|
|
259
|
+
section_name = colon_parts[0]
|
|
260
|
+
sub_key = ":".join(colon_parts[1:])
|
|
261
|
+
self.expect("EQUALS")
|
|
262
|
+
val = self.parse_value_rest()
|
|
263
|
+
body = [Directive(sub_key, [val], t.line, 0)]
|
|
264
|
+
return Section(section_name, body, t.line)
|
|
265
|
+
|
|
266
|
+
# Handle colon-separated section:key = value (two parts)
|
|
267
|
+
if len(colon_parts) == 2:
|
|
268
|
+
section_name = colon_parts[0]
|
|
269
|
+
inner_key = colon_parts[1]
|
|
270
|
+
self.expect("EQUALS")
|
|
271
|
+
val = self.parse_value_rest()
|
|
272
|
+
body = [Directive(inner_key, [val], t.line, 0)]
|
|
273
|
+
return Section(section_name, body, t.line)
|
|
274
|
+
|
|
275
|
+
return self.parse_general_directive(directive, t.line)
|
|
276
|
+
|
|
277
|
+
def parse_general_directive(self, directive: str, line: int) -> Optional[BlockStmt]:
|
|
278
|
+
t = self.peek()
|
|
279
|
+
if t.type == "EQUALS":
|
|
280
|
+
self.advance()
|
|
281
|
+
values = self.parse_comma_values()
|
|
282
|
+
return Directive(directive, values, line, 0)
|
|
283
|
+
if t.type == "BLOCK_OPEN":
|
|
284
|
+
self.advance()
|
|
285
|
+
body = self.parse_block(stop="BLOCK_CLOSE")
|
|
286
|
+
self.expect("BLOCK_CLOSE")
|
|
287
|
+
return Section(directive, body, line)
|
|
288
|
+
raise ParserError(f"Expected = or {{ after {directive!r}", t)
|
|
289
|
+
|
|
290
|
+
def parse_bind(self, directive: str, line: int) -> BindDirective:
|
|
291
|
+
flags = _parse_combined_bind(directive)
|
|
292
|
+
self.expect("EQUALS")
|
|
293
|
+
self.skip_newlines()
|
|
294
|
+
values = self.parse_comma_values()
|
|
295
|
+
if len(values) < 3:
|
|
296
|
+
raise ParserError(f"bind needs at least 3 arguments (mods, key, dispatcher), got {len(values)}", Token("IDENT", directive, line, 0))
|
|
297
|
+
mods_str = values[0].strip()
|
|
298
|
+
key = values[1].strip()
|
|
299
|
+
dispatcher = values[2].strip()
|
|
300
|
+
if dispatcher in ("exec", "execr") and len(values) > 3:
|
|
301
|
+
cmd = ",".join(v.strip() for v in values[3:]).strip()
|
|
302
|
+
params = [cmd] if cmd else []
|
|
303
|
+
else:
|
|
304
|
+
params = [v.strip() for v in values[3:]]
|
|
305
|
+
mods = [m.strip() for m in mods_str.replace(",", " ").split()] if mods_str else []
|
|
306
|
+
return BindDirective(mods, key, dispatcher, params, flags, line)
|
|
307
|
+
|
|
308
|
+
def parse_monitor(self, line: int) -> MonitorDirective:
|
|
309
|
+
self.expect("EQUALS")
|
|
310
|
+
values = self.parse_comma_values()
|
|
311
|
+
name = values[0].strip() if len(values) > 0 else ""
|
|
312
|
+
mode = values[1].strip() if len(values) > 1 else "preferred"
|
|
313
|
+
position = values[2].strip() if len(values) > 2 else "auto"
|
|
314
|
+
scale = values[3].strip() if len(values) > 3 else "1"
|
|
315
|
+
extra = {}
|
|
316
|
+
for i in range(4, len(values), 2):
|
|
317
|
+
key = values[i].strip()
|
|
318
|
+
val = values[i + 1].strip() if i + 1 < len(values) else "true"
|
|
319
|
+
extra[key] = val
|
|
320
|
+
return MonitorDirective(name, mode, position, scale, line, extra)
|
|
321
|
+
|
|
322
|
+
def _parse_rule_block_body(self) -> tuple:
|
|
323
|
+
"""Parse windowrule/layerrule block content { ... }.
|
|
324
|
+
Returns (name, match dict, effects dict)."""
|
|
325
|
+
name = ""
|
|
326
|
+
match: Dict[str, str] = {}
|
|
327
|
+
effects: Dict[str, List[str]] = {}
|
|
328
|
+
|
|
329
|
+
self.skip_newlines()
|
|
330
|
+
self.expect("BLOCK_OPEN")
|
|
331
|
+
self.skip_newlines()
|
|
332
|
+
|
|
333
|
+
while self.peek().type not in ("BLOCK_CLOSE", "EOF"):
|
|
334
|
+
t = self.peek()
|
|
335
|
+
if t.type == "COMMENT":
|
|
336
|
+
self.advance()
|
|
337
|
+
elif t.type == "IDENT":
|
|
338
|
+
key_parts = [self.advance().value]
|
|
339
|
+
while self.peek().type == "COLON":
|
|
340
|
+
self.advance()
|
|
341
|
+
if self.peek().type == "IDENT":
|
|
342
|
+
key_parts.append(self.advance().value)
|
|
343
|
+
else:
|
|
344
|
+
break
|
|
345
|
+
key = ":".join(key_parts)
|
|
346
|
+
|
|
347
|
+
self.expect("EQUALS")
|
|
348
|
+
val = self.parse_value_rest()
|
|
349
|
+
|
|
350
|
+
if key == "name":
|
|
351
|
+
name = val
|
|
352
|
+
elif key.startswith("match:"):
|
|
353
|
+
match[key[len("match:"):]] = val
|
|
354
|
+
else:
|
|
355
|
+
effects[key] = [val]
|
|
356
|
+
else:
|
|
357
|
+
self.advance()
|
|
358
|
+
self.skip_newlines()
|
|
359
|
+
|
|
360
|
+
self.expect("BLOCK_CLOSE")
|
|
361
|
+
return name, match, effects
|
|
362
|
+
|
|
363
|
+
def parse_windowrule(self, is_v2: bool, line: int) -> Union[WindowRule, WindowRuleBlock]:
|
|
364
|
+
if self.peek().type == "BLOCK_OPEN":
|
|
365
|
+
name, match, effects = self._parse_rule_block_body()
|
|
366
|
+
return WindowRuleBlock(is_v2, name, match, effects, line)
|
|
367
|
+
self.expect("EQUALS")
|
|
368
|
+
values = self.parse_comma_values()
|
|
369
|
+
rule = values[0].strip() if values else ""
|
|
370
|
+
match_params = [v.strip() for v in values[1:]]
|
|
371
|
+
return WindowRule(is_v2, rule, match_params, line)
|
|
372
|
+
|
|
373
|
+
def parse_exec(self, directive: str, line: int) -> ExecDirective:
|
|
374
|
+
self.expect("EQUALS")
|
|
375
|
+
command = self.parse_line_rest()
|
|
376
|
+
return ExecDirective(directive, command, line)
|
|
377
|
+
|
|
378
|
+
def parse_line_rest(self) -> str:
|
|
379
|
+
parts = []
|
|
380
|
+
while self.peek().type not in ("NEWLINE", "EOF", "COMMENT", "BLOCK_CLOSE"):
|
|
381
|
+
if self.peek().type == "DOLLAR":
|
|
382
|
+
self.advance()
|
|
383
|
+
var_t = self.expect("IDENT")
|
|
384
|
+
parts.append("$" + var_t.value)
|
|
385
|
+
else:
|
|
386
|
+
t = self.advance()
|
|
387
|
+
parts.append(t.value)
|
|
388
|
+
return self._join_tokens(parts)
|
|
389
|
+
|
|
390
|
+
def parse_animation(self, line: int) -> AnimationDirective:
|
|
391
|
+
self.expect("EQUALS")
|
|
392
|
+
values = self.parse_comma_values()
|
|
393
|
+
name = values[0].strip() if len(values) > 0 else ""
|
|
394
|
+
style = values[1].strip() if len(values) > 1 else ""
|
|
395
|
+
speed = values[2].strip() if len(values) > 2 else "1"
|
|
396
|
+
curve = values[3].strip() if len(values) > 3 else "default"
|
|
397
|
+
return AnimationDirective(name, style, speed, curve, line)
|
|
398
|
+
|
|
399
|
+
def parse_bezier(self, line: int) -> BezierDirective:
|
|
400
|
+
self.expect("EQUALS")
|
|
401
|
+
values = self.parse_comma_values()
|
|
402
|
+
name = values[0].strip() if len(values) > 0 else ""
|
|
403
|
+
p1x = values[1].strip() if len(values) > 1 else "0"
|
|
404
|
+
p1y = values[2].strip() if len(values) > 2 else "0"
|
|
405
|
+
p2x = values[3].strip() if len(values) > 3 else "1"
|
|
406
|
+
p2y = values[4].strip() if len(values) > 4 else "1"
|
|
407
|
+
return BezierDirective(name, p1x, p1y, p2x, p2y, line)
|
|
408
|
+
|
|
409
|
+
def parse_env(self, line: int) -> EnvDirective:
|
|
410
|
+
self.expect("EQUALS")
|
|
411
|
+
name_t = self.expect("IDENT")
|
|
412
|
+
name = name_t.value.strip()
|
|
413
|
+
val = ""
|
|
414
|
+
if self.peek().type == "COMMA":
|
|
415
|
+
self.advance()
|
|
416
|
+
val = self.parse_line_rest()
|
|
417
|
+
return EnvDirective(name, val, line)
|
|
418
|
+
|
|
419
|
+
def parse_source(self, line: int) -> SourceDirective:
|
|
420
|
+
self.expect("EQUALS")
|
|
421
|
+
values = self.parse_comma_values()
|
|
422
|
+
path = values[0].strip() if values else ""
|
|
423
|
+
return SourceDirective(path, line)
|
|
424
|
+
|
|
425
|
+
def parse_gesture(self, line: int) -> GestureDirective:
|
|
426
|
+
self.skip_newlines()
|
|
427
|
+
self.expect("BLOCK_OPEN")
|
|
428
|
+
body = []
|
|
429
|
+
self.skip_newlines()
|
|
430
|
+
while self.peek().type not in ("BLOCK_CLOSE", "EOF"):
|
|
431
|
+
t = self.peek()
|
|
432
|
+
if t.type == "COMMENT":
|
|
433
|
+
body.append(Comment(t.value, t.line))
|
|
434
|
+
self.advance()
|
|
435
|
+
elif t.type == "IDENT":
|
|
436
|
+
key_t = self.advance()
|
|
437
|
+
self.expect("EQUALS")
|
|
438
|
+
val = self.parse_value_rest()
|
|
439
|
+
body.append(Directive(key_t.value, [val], key_t.line, 0))
|
|
440
|
+
else:
|
|
441
|
+
self.advance()
|
|
442
|
+
self.skip_newlines()
|
|
443
|
+
self.expect("BLOCK_CLOSE")
|
|
444
|
+
return GestureDirective(body, line)
|
|
445
|
+
|
|
446
|
+
def parse_device(self, name: str, line: int) -> DeviceSection:
|
|
447
|
+
self.skip_newlines()
|
|
448
|
+
self.expect("BLOCK_OPEN")
|
|
449
|
+
body = []
|
|
450
|
+
self.skip_newlines()
|
|
451
|
+
while self.peek().type not in ("BLOCK_CLOSE", "EOF"):
|
|
452
|
+
t = self.peek()
|
|
453
|
+
if t.type == "COMMENT":
|
|
454
|
+
body.append(Comment(t.value, t.line))
|
|
455
|
+
self.advance()
|
|
456
|
+
elif t.type == "IDENT":
|
|
457
|
+
key_t = self.advance()
|
|
458
|
+
self.expect("EQUALS")
|
|
459
|
+
val = self.parse_value_rest()
|
|
460
|
+
body.append(Directive(key_t.value, [val], key_t.line, 0))
|
|
461
|
+
else:
|
|
462
|
+
self.advance()
|
|
463
|
+
self.skip_newlines()
|
|
464
|
+
self.expect("BLOCK_CLOSE")
|
|
465
|
+
return DeviceSection(name, body, line)
|
|
466
|
+
|
|
467
|
+
def parse_workspace(self, line: int) -> WorkspaceDirective:
|
|
468
|
+
self.expect("EQUALS")
|
|
469
|
+
values = self.parse_comma_values()
|
|
470
|
+
name = values[0].strip() if values else ""
|
|
471
|
+
params = [v.strip() for v in values[1:]]
|
|
472
|
+
return WorkspaceDirective(name, params, line)
|
|
473
|
+
|
|
474
|
+
def parse_layerrule(self, line: int) -> Union[LayerRuleDirective, LayerRuleBlock]:
|
|
475
|
+
if self.peek().type == "BLOCK_OPEN":
|
|
476
|
+
name, match, effects = self._parse_rule_block_body()
|
|
477
|
+
return LayerRuleBlock(name, match, effects, line)
|
|
478
|
+
self.expect("EQUALS")
|
|
479
|
+
values = self.parse_comma_values()
|
|
480
|
+
rule = values[0].strip() if values else ""
|
|
481
|
+
namespace = values[1].strip() if len(values) > 1 else ""
|
|
482
|
+
return LayerRuleDirective(rule, namespace, line)
|
|
483
|
+
|
|
484
|
+
def parse_submap(self, line: int) -> Directive:
|
|
485
|
+
self.expect("EQUALS")
|
|
486
|
+
values = self.parse_comma_values()
|
|
487
|
+
return Directive("submap", values, line, 0)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def parse_config(source: str) -> ConfigFile:
|
|
491
|
+
from hyprconf2lua.lexer import tokenize
|
|
492
|
+
tokens = tokenize(source)
|
|
493
|
+
parser = Parser(tokens)
|
|
494
|
+
return parser.parse()
|