lib/xkb/src/keymap/text/parse/state.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const xkb = @import("../../../root.zig");
3 const text_root = @import("../root.zig");
4 const document = text_root.document;
5 const lexer = text_root.lexer;
6
7 pub const Error = std.mem.Allocator.Error || lexer.Error || error{
8 InvalidEncoding,
9 InvalidSyntax,
10 TooManyModifiers,
11 };
12
13 pub const MaskKind = enum {
14 real,
15 virtual,
16 both,
17 };
18
19 pub const Parser = struct {
20 allocator: std.mem.Allocator,
21 lexer_value: lexer.Lexer,
22 lookahead: ?lexer.Token = null,
23 keycodes: std.ArrayListUnmanaged(document.Keycode) = .empty,
24 aliases: std.ArrayListUnmanaged(document.Alias) = .empty,
25 virtual_modifiers: std.ArrayListUnmanaged(document.VirtualModifier) = .empty,
26 types: std.ArrayListUnmanaged(document.KeyType) = .empty,
27 interprets: std.ArrayListUnmanaged(document.Interpret) = .empty,
28 keys: std.ArrayListUnmanaged(document.Key) = .empty,
29 modifier_maps: std.ArrayListUnmanaged(document.ModifierMap) = .empty,
30 default_repeat: bool = false,
31 default_level_one_only: bool = false,
32
33 pub fn init(allocator: std.mem.Allocator, input: []const u8) Error!Parser {
34 if (!std.unicode.utf8ValidateSlice(input) or std.mem.indexOfScalar(u8, input, 0) != null) {
35 return error.InvalidEncoding;
36 }
37 return .{
38 .allocator = allocator,
39 .lexer_value = .{ .input = input },
40 };
41 }
42
43 pub fn finish(self: *Parser) Error!document.Document {
44 return .{
45 .keycodes = try self.keycodes.toOwnedSlice(self.allocator),
46 .aliases = try self.aliases.toOwnedSlice(self.allocator),
47 .virtual_modifiers = try self.virtual_modifiers.toOwnedSlice(self.allocator),
48 .types = try self.types.toOwnedSlice(self.allocator),
49 .interprets = try self.interprets.toOwnedSlice(self.allocator),
50 .keys = try self.keys.toOwnedSlice(self.allocator),
51 .modifier_maps = try self.modifier_maps.toOwnedSlice(self.allocator),
52 };
53 }
54
55 pub fn peek(self: *Parser) Error!lexer.Token {
56 if (self.lookahead == null) self.lookahead = try self.lexer_value.next();
57 return self.lookahead.?;
58 }
59
60 pub fn next(self: *Parser) Error!lexer.Token {
61 if (self.lookahead) |token| {
62 self.lookahead = null;
63 return token;
64 }
65 return self.lexer_value.next();
66 }
67
68 pub fn take(self: *Parser, tag: lexer.Tag) Error!bool {
69 if ((try self.peek()).tag != tag) return false;
70 _ = try self.next();
71 return true;
72 }
73
74 pub fn expect(self: *Parser, tag: lexer.Tag) Error!lexer.Token {
75 const token = try self.next();
76 if (token.tag != tag) return error.InvalidSyntax;
77 return token;
78 }
79
80 pub fn expectKeyword(self: *Parser, keyword: []const u8) Error!void {
81 const token = try self.expect(.word);
82 if (!keywordEqual(token.text, keyword)) return error.InvalidSyntax;
83 }
84
85 pub fn takeKeyword(self: *Parser, keyword: []const u8) Error!bool {
86 const token = try self.peek();
87 if (token.tag != .word or !keywordEqual(token.text, keyword)) return false;
88 _ = try self.next();
89 return true;
90 }
91
92 pub fn number(self: *Parser) Error!u32 {
93 const token = try self.expect(.word);
94 return parseNumber(token.text) orelse error.InvalidSyntax;
95 }
96
97 pub fn level(self: *Parser) Error!usize {
98 const token = try self.expect(.word);
99 const raw = if (startsWithIgnoreCase(token.text, "Level"))
100 parseNumber(token.text["Level".len..]) orelse return error.InvalidSyntax
101 else
102 parseNumber(token.text) orelse return error.InvalidSyntax;
103 if (raw == 0) return error.InvalidSyntax;
104 return raw - 1;
105 }
106
107 pub fn group(self: *Parser) Error!usize {
108 const token = try self.expect(.word);
109 const raw = if (startsWithIgnoreCase(token.text, "Group"))
110 parseNumber(token.text["Group".len..]) orelse return error.InvalidSyntax
111 else
112 parseNumber(token.text) orelse return error.InvalidSyntax;
113 if (raw == 0) return error.InvalidSyntax;
114 return raw - 1;
115 }
116
117 pub fn boolean(self: *Parser) Error!bool {
118 const token = try self.expect(.word);
119 if (keywordEqual(token.text, "true") or keywordEqual(token.text, "yes") or
120 keywordEqual(token.text, "on")) return true;
121 if (keywordEqual(token.text, "false") or keywordEqual(token.text, "no") or
122 keywordEqual(token.text, "off")) return false;
123 return error.InvalidSyntax;
124 }
125
126 pub fn keysym(self: *Parser) Error!?xkb.keysym.Keysym {
127 const token = try self.expect(.word);
128 if (keywordEqual(token.text, "NoSymbol")) return null;
129 const raw = parseNumber(token.text) orelse {
130 const value = xkb.keysym.fromName(token.text) orelse return error.InvalidSyntax;
131 if (value == .no_symbol) return null;
132 return value;
133 };
134 if (raw == 0) return null;
135 if (raw > xkb.keysym.max) return error.InvalidSyntax;
136 return @fromBackingInt(@intCast(raw));
137 }
138
139 pub fn mask(self: *Parser, kind: MaskKind) Error!u32 {
140 var result: u32 = 0;
141 var had_value = false;
142 while (true) {
143 const token = try self.expect(.word);
144 result |= try self.maskTerm(token.text, kind);
145 had_value = true;
146 if (!try self.take(.plus)) break;
147 }
148 if (!had_value) return error.InvalidSyntax;
149 return result;
150 }
151
152 pub fn virtualModifierDeclaration(self: *Parser) Error!void {
153 while (true) {
154 const name = (try self.expect(.word)).text;
155 var mapping: ?u32 = null;
156 if (try self.take(.equal)) mapping = try self.mask(.both);
157 try self.addVirtualModifier(name, mapping);
158 if (!try self.take(.comma)) break;
159 }
160 _ = try self.expect(.semicolon);
161 }
162
163 pub fn modifierIndex(self: *const Parser, name: []const u8, kind: MaskKind) ?usize {
164 if (kind != .virtual) {
165 for (xkb.keymap.core_modifiers, 0..) |modifier, index| {
166 if (keywordEqual(name, modifier.name)) return index;
167 }
168 }
169 if (kind != .real) {
170 for (self.virtual_modifiers.items, 0..) |modifier, offset| {
171 if (keywordEqual(name, modifier.name)) {
172 return xkb.keymap.core_modifier_count + offset;
173 }
174 }
175 }
176 return null;
177 }
178
179 pub fn addVirtualModifier(
180 self: *Parser,
181 name: []const u8,
182 mapping: ?u32,
183 ) Error!void {
184 for (self.virtual_modifiers.items) |*modifier| {
185 if (!keywordEqual(name, modifier.name)) continue;
186 if (mapping) |value| {
187 if (modifier.explicit_mapping) |existing| {
188 if (existing != value) return error.InvalidSyntax;
189 } else modifier.explicit_mapping = value;
190 }
191 return;
192 }
193 if (xkb.keymap.core_modifier_count + self.virtual_modifiers.items.len ==
194 @bitSizeOf(xkb.keymap.ModifierMask)) return error.TooManyModifiers;
195 try self.virtual_modifiers.append(self.allocator, .{
196 .name = name,
197 .explicit_mapping = mapping,
198 });
199 }
200
201 pub fn skipStatement(self: *Parser) Error!void {
202 var braces: usize = 0;
203 var brackets: usize = 0;
204 var parens: usize = 0;
205 while (true) {
206 const token = try self.next();
207 switch (token.tag) {
208 .left_brace => braces += 1,
209 .right_brace => {
210 if (braces == 0) return error.InvalidSyntax;
211 braces -= 1;
212 },
213 .left_bracket => brackets += 1,
214 .right_bracket => {
215 if (brackets == 0) return error.InvalidSyntax;
216 brackets -= 1;
217 },
218 .left_paren => parens += 1,
219 .right_paren => {
220 if (parens == 0) return error.InvalidSyntax;
221 parens -= 1;
222 },
223 .semicolon => if (braces == 0 and brackets == 0 and parens == 0) return,
224 .end => return error.InvalidSyntax,
225 else => {},
226 }
227 }
228 }
229
230 pub fn skipBlock(self: *Parser) Error!void {
231 var depth: usize = 1;
232 while (depth != 0) {
233 switch ((try self.next()).tag) {
234 .left_brace => depth += 1,
235 .right_brace => depth -= 1,
236 .end => return error.InvalidSyntax,
237 else => {},
238 }
239 }
240 }
241
242 pub fn skipValue(self: *Parser) Error!void {
243 var braces: usize = 0;
244 var brackets: usize = 0;
245 var parens: usize = 0;
246 while (true) {
247 const token = try self.peek();
248 if (braces == 0 and brackets == 0 and parens == 0 and
249 (token.tag == .comma or token.tag == .right_brace or token.tag == .semicolon)) return;
250 _ = try self.next();
251 switch (token.tag) {
252 .left_brace => braces += 1,
253 .right_brace => {
254 if (braces == 0) return error.InvalidSyntax;
255 braces -= 1;
256 },
257 .left_bracket => brackets += 1,
258 .right_bracket => {
259 if (brackets == 0) return error.InvalidSyntax;
260 brackets -= 1;
261 },
262 .left_paren => parens += 1,
263 .right_paren => {
264 if (parens == 0) return error.InvalidSyntax;
265 parens -= 1;
266 },
267 .end => return error.InvalidSyntax,
268 else => {},
269 }
270 }
271 }
272
273 fn maskTerm(self: *Parser, text: []const u8, kind: MaskKind) Error!u32 {
274 if (keywordEqual(text, "none")) return 0;
275 if (keywordEqual(text, "all")) return xkb.keymap.core_modifier_mask;
276 if (parseNumber(text)) |value| {
277 const allowed = switch (kind) {
278 .real => xkb.keymap.core_modifier_mask,
279 .virtual => ~xkb.keymap.core_modifier_mask,
280 .both => std.math.maxInt(u32),
281 };
282 if (value & ~allowed != 0) return error.InvalidSyntax;
283 return value;
284 }
285 const index = self.modifierIndex(text, kind) orelse return error.InvalidSyntax;
286 return xkb.keymap.modifierBit(index);
287 }
288 };
289
290 pub fn keywordEqual(left: []const u8, right: []const u8) bool {
291 return std.ascii.eqlIgnoreCase(left, right);
292 }
293
294 pub fn parseNumber(text: []const u8) ?u32 {
295 if (text.len == 0) return null;
296 return std.fmt.parseInt(u32, text, 0) catch null;
297 }
298
299 fn startsWithIgnoreCase(text: []const u8, prefix: []const u8) bool {
300 return text.len >= prefix.len and std.ascii.eqlIgnoreCase(text[0..prefix.len], prefix);
301 }