1#
2# $Id: xkbparser.rb,v 1.1.1.1 2022/10/17 07:37:08 mrg Exp $
3#
4# Commont parsing classes for symbols/inet
5# The parsing is simplified, based on regex - it is NOT a real parser for very
6# complex XKB format
7#
8
9require "utils.rb"
10
11class Symbols < Hash
12
13  #
14  # Constructor
15  #
16  def initialize
17    @includedSyms = Array.new
18  end
19
20  # Write-only property, parent list of symbols definitions
21  def symbols_list=(symbolsList)
22    @symbolsList = symbolsList
23  end
24
25  # Whether this set of symbols is hidden or not
26  def hidden?
27    @hidden
28  end
29
30  def hidden=(h)
31    @hidden = h
32  end
33
34  #
35  # Add "dependency" - the symbols referenced using the "include" statement.
36  #
37  def add_included(other)
38    @includedSyms.push(other)
39  end
40
41  alias get_original []
42  alias keys_original keys
43
44  #
45  # Get the symbol, trying first own definitions, then walking through all 
46  # dependenies
47  #
48  def [](symName)
49    own = self.get_original(symName)
50    if own.nil?
51      @includedSyms.find_all do | symsName |
52        syms = @symbolsList[symsName]
53        his = syms[symName]
54        if !his.nil?
55          own = his
56          break
57        end
58      end
59    end
60    own
61  end
62
63  #
64  # All keys - including the ones specified in the included sections
65  #
66  def keys()
67    @includedSyms.inject(keys_original) do | rv, symsName |
68      syms = @symbolsList[symsName]
69      rv | syms.keys
70    end
71  end
72
73  # Size of all keys
74  def length()
75    keys().length()
76  end
77
78  #
79  # Size - takes into account overlapping key definitions
80  #
81  def size()
82    keys.size()
83  end
84
85  #
86  # Create a hash including all elements of this hash which are not in the
87  # other hash, use symbols + and * for marking the elements which existed in
88  # the original hash (+ if not existed)
89  #
90  def -(other)
91    diff = self.class.new
92    self.find_all do | key, value | 
93      existing = other[key]
94      if existing != value
95        diff[key] = [ value, existing.nil? ? '+' : '' ]
96      end 
97    end
98    diff
99  end
100
101
102  def to_s
103    s = "{\n"
104    # First output included syms
105    @includedSyms.find_all do | symsName |
106       s += "  include \"inet(#{symsName})\"\n"
107    end
108    # Then - own definitions
109    self.find_all do | key, value |
110       s += "  key #{key} { [ #{value} ] };\n"
111    end
112    s + "}";
113  end
114
115end
116
117class SymbolsList < Hash
118
119  #
120  # Add new xkb_symbols
121  #
122  def add_symbols (symbolsName, hidden)
123    newSyms = Symbols.new
124    newSyms.symbols_list = self
125    newSyms.hidden = hidden
126    self[symbolsName] = newSyms
127  end
128
129  def to_s
130    s = "// Autogenerated\n\n"
131    self.find_all do | symbols, mapping |
132      s += "partial alphanumeric_keys\nxkb_symbols \"#{symbols}\" #{mapping};\n\n" 
133    end
134    s
135  end
136
137  def match_symbols(new_symbols,limit)
138    matching = Hash.new
139    find_all do | symbols, mapping |
140      diff = new_symbols - mapping
141      if diff.size <= limit
142        matching[symbols] = diff
143      end
144    end
145    matching
146  end
147
148  def merge()
149    everything = NonuniqueCountingHash.new
150    find_all do | symsName, syms |
151      syms.find_all do | symName, keycode |
152        everything[symName] = keycode
153      end
154    end
155    everything
156  end
157
158end
159
160class Parser
161
162  def parse (fileName)
163    allSyms = SymbolsList.new;
164    currentSyms = nil
165    hidden = false
166    File.open(fileName) do | file |
167      file.each_line do | line |
168        line.scan(/xkb_symbols\s+"(\w+)"/) do | symsName |
169          currentSyms = allSyms.add_symbols(symsName[0], hidden)
170        end
171        line.scan(/^\s*key\s*<(\w+)>\s*\{\s*\[\s*(\w+)/) do | keycode, keysym |
172          currentSyms[keycode] = keysym
173        end
174        line.scan(/^partial\s+(hidden\s+)?alphanumeric_keys/) do | h |
175          hidden = !h[0].nil?
176        end
177        line.scan(/^\s*include\s+"inet\((\w+)\)"/) do | otherPart |
178          currentSyms.add_included(otherPart[0])
179        end
180      end
181    end
182    allSyms
183  end
184
185end
186