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 }