summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2021-04-12 16:25:31 +0000
committerSergey Udaltsov <sergey.udaltsov@gmail.com>2021-04-12 16:25:31 +0000
commitd04df117004581ec1431a2bc4147b7c2586d002d (patch)
tree7abafe7a9d1d06d40b5c850d19d6ea6660f0a7fc /scripts
parentf06af46e8be7b351a459ad0af50407d690c22c3c (diff)
downloadxkeyboard-config-d04df117004581ec1431a2bc4147b7c2586d002d.tar.gz
symbols: remove whitespaces before xkb_symbols declarations
No functional effect but it makes simple parsers easier and grep more reliable. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/symbols-tree.py197
1 files changed, 197 insertions, 0 deletions
diff --git a/scripts/symbols-tree.py b/scripts/symbols-tree.py
new file mode 100755
index 0000000..d0f9007
--- /dev/null
+++ b/scripts/symbols-tree.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python3
+#
+# Builds a tree view of a symbols file (showing all includes)
+#
+# This file is formatted with Python Black
+
+import argparse
+import pathlib
+import os
+from pyparsing import (
+ Word,
+ Literal,
+ LineEnd,
+ OneOrMore,
+ oneOf,
+ Or,
+ And,
+ QuotedString,
+ Regex,
+ cppStyleComment,
+ alphanums,
+ Optional,
+ ParseException,
+)
+
+xkb_basedir = None
+
+
+class XkbSymbols:
+ def __init__(self, file, name):
+ self.file = file # Path to the file this section came from
+ self.layout = file.name # XKb - filename is the layout name
+ self.name = name
+ self.includes = []
+
+ def __str__(self):
+ return f"{self.layout}({self.name}): {self.includes}"
+
+
+class XkbLoader:
+ """
+ Wrapper class to avoid loading the same symbols file over and over
+ again.
+ """
+
+ class XkbParserException(Exception):
+ pass
+
+ _instance = None
+
+ def __init__(self, xkb_basedir):
+ self.xkb_basedir = xkb_basedir
+ self.loaded = {}
+
+ @classmethod
+ def create(cls, xkb_basedir):
+ assert cls._instance is None
+ cls._instance = XkbLoader(xkb_basedir)
+
+ @classmethod
+ def instance(cls):
+ assert cls._instance is not None
+ return cls._instance
+
+ @classmethod
+ def load_symbols(cls, file):
+ return cls.instance().load_symbols_file(file)
+
+ def load_symbols_file(self, file):
+ file = self.xkb_basedir / file
+ try:
+ return self.loaded[file]
+ except KeyError:
+ pass
+
+ sections = []
+
+ def quoted(name):
+ return QuotedString(quoteChar='"', unquoteResults=True)
+
+ # Callback, toks[0] is "foo" for xkb_symbols "foo"
+ def new_symbols_section(name, loc, toks):
+ assert len(toks) == 1
+ sections.append(XkbSymbols(file, toks[0]))
+
+ # Callback, toks[0] is "foo(bar)" for include "foo(bar)"
+ def append_includes(name, loc, toks):
+ assert len(toks) == 1
+ sections[-1].includes.append(toks[0])
+
+ EOL = LineEnd().suppress()
+ SECTIONTYPE = (
+ "default",
+ "partial",
+ "hidden",
+ "alphanumeric_keys",
+ "modifier_keys",
+ "keypad_keys",
+ "function_keys",
+ "alternate_group",
+ )
+ NAME = quoted("name").setParseAction(new_symbols_section)
+ INCLUDE = (
+ lit("include") + quoted("include").setParseAction(append_includes) + EOL
+ )
+ # We only care about includes
+ OTHERLINE = And([~lit("};"), ~lit("include") + Regex(".*")]) + EOL
+
+ with open(file) as fd:
+ types = OneOrMore(oneOf(SECTIONTYPE)).suppress()
+ include_or_other = Or([INCLUDE, OTHERLINE.suppress()])
+ section = (
+ types
+ + lit("xkb_symbols")
+ + NAME
+ + lit("{")
+ + OneOrMore(include_or_other)
+ + lit("};")
+ )
+ grammar = OneOrMore(section)
+ grammar.ignore(cppStyleComment)
+ try:
+ result = grammar.parseFile(fd)
+ except ParseException as e:
+ raise XkbLoader.XkbParserException(str(e))
+
+ self.loaded[file] = sections
+
+ return sections
+
+
+def lit(string):
+ return Literal(string).suppress()
+
+
+def print_section(s, filter_section=None, indent=0):
+ if filter_section and s.name != filter_section:
+ return
+
+ layout = Word(alphanums + "_/").setResultsName("layout")
+ variant = Optional(
+ lit("(") + Word(alphanums + "_").setResultsName("variant") + lit(")")
+ )
+ grammar = layout + variant
+
+ prefix = ""
+ if indent > 0:
+ prefix = " " * (indent - 2) + "|-> "
+ print(f"{prefix}{s.layout}({s.name})")
+ for include in s.includes:
+ result = grammar.parseString(include)
+ # Should really find the "default" section but for this script
+ # hardcoding "basic" is good enough
+ layout, variant = result.layout, result.variant or "basic"
+
+ # include "foo(bar)" means file "foo", section bar
+ includefile = xkb_basedir / layout
+ include_sections = XkbLoader.load_symbols(layout)
+ for include_section in include_sections:
+ print_section(include_section, filter_section=variant, indent=indent + 4)
+
+
+def list_sections(sections, filter_section=None, indent=0):
+ for section in sections:
+ print_section(section, filter_section)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="XKB symbol tree viewer")
+ parser.add_argument(
+ "file",
+ metavar="file-or-directory",
+ type=pathlib.Path,
+ help="The XKB symbols file or directory",
+ )
+ parser.add_argument(
+ "section", type=str, default=None, nargs="?", help="The section (optional)"
+ )
+ ns = parser.parse_args()
+
+ if ns.file.is_dir():
+ xkb_basedir = ns.file.resolve()
+ files = sorted([f for f in ns.file.iterdir() if not f.is_dir()])
+ else:
+ # Note: this requires that the file given on the cmdline is not one of
+ # the sun_vdr/de or others inside a subdirectory. meh.
+ xkb_basedir = ns.file.parent.resolve()
+ files = [ns.file]
+
+ XkbLoader.create(xkb_basedir)
+
+ for file in files:
+ try:
+ sections = XkbLoader.load_symbols(file.resolve())
+ list_sections(sections, filter_section=ns.section)
+ except XkbLoader.XkbParserException:
+ pass