aboutsummaryrefslogtreecommitdiff
path: root/users/dennytom/chording_engine/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'users/dennytom/chording_engine/parser.py')
-rw-r--r--users/dennytom/chording_engine/parser.py231
1 files changed, 231 insertions, 0 deletions
diff --git a/users/dennytom/chording_engine/parser.py b/users/dennytom/chording_engine/parser.py
new file mode 100644
index 000000000..b62cf007e
--- /dev/null
+++ b/users/dennytom/chording_engine/parser.py
@@ -0,0 +1,231 @@
1#!/usr/bin/env python3
2
3import json
4from functools import reduce
5from chord import *
6import sys
7
8comma_separator = (lambda x, y: str(x) + ", " + str(y))
9string_sum = (lambda x, y: str(x) + " + " + str(y))
10newline_separator = (lambda x, y: str(x) + "\n" + str(y))
11
12def add_includes(data):
13 output_buffer = ""
14 if not ("do_not_include_QMK" in data["parameters"] and data["parameters"]["do_not_include_QMK"] == True):
15 output_buffer += "#include QMK_KEYBOARD_H\n"
16 if len(data["extra_dependencies"]) > 0:
17 for dependecy in data["extra_dependencies"]:
18 output_buffer += '#include "' + dependecy + '"\n'
19
20 return output_buffer + "\n"
21
22def add_parameters(data):
23 output_buffer = ""
24
25 number_of_keys = len(data["keys"])
26 if number_of_keys <= 8:
27 hash_type = "uint8_t"
28 elif number_of_keys <= 16:
29 hash_type = "uint16_t"
30 elif number_of_keys <= 32:
31 hash_type = "uint32_t"
32 elif number_of_keys <= 64:
33 hash_type = "uint64_t"
34 else:
35 raise Exception("The engine currently supports only up to 64 keys.")
36
37 output_buffer += "#define CHORD_TIMEOUT " + str(data["parameters"]["chord_timeout"]) + "\n"
38 output_buffer += "#define DANCE_TIMEOUT " + str(data["parameters"]["dance_timeout"]) + "\n"
39 output_buffer += "#define LEADER_TIMEOUT " + str(data["parameters"]["leader_timeout"]) + "\n"
40 output_buffer += "#define TAP_TIMEOUT " + str(data["parameters"]["tap_timeout"]) + "\n"
41 output_buffer += "#define LONG_PRESS_MULTIPLIER " + str(data["parameters"]["long_press_multiplier"]) + "\n"
42 output_buffer += "#define DYNAMIC_MACRO_MAX_LENGTH " + str(data["parameters"]["dynamic_macro_max_length"]) + "\n"
43 output_buffer += "#define COMMAND_MAX_LENGTH " + str(data["parameters"]["command_max_length"]) + "\n"
44 output_buffer += "#define STRING_MAX_LENGTH " + str(data["parameters"]["string_max_length"]) + "\n"
45 output_buffer += "#define LEADER_MAX_LENGTH " + str(data["parameters"]["leader_max_length"]) + "\n"
46 output_buffer += "#define HASH_TYPE " + hash_type + "\n"
47 output_buffer += "#define NUMBER_OF_KEYS " + str(len(data["keys"])) + "\n"
48 output_buffer += "#define DEFAULT_PSEUDOLAYER " + data["parameters"]["default_pseudolayer"] + "\n"
49
50 return output_buffer + "\n"
51
52def add_keycodes(data):
53 output_buffer = ""
54
55 if not len(data["keys"]) == len(set(data["keys"])):
56 raise Exception("The keys must have unique names")
57
58 for key, counter in zip(data["keys"], range(0, len(data["keys"]))):
59 output_buffer += "#define H_" + key + " ((HASH_TYPE) 1 << " + str(counter) + ")\n"
60 output_buffer += "\n"
61
62 output_buffer += "enum internal_keycodes {\n"
63 output_buffer += " " + data["keys"][0] + " = SAFE_RANGE,\n"
64 output_buffer += " " + reduce(comma_separator, [key for key in data["keys"][1:]]) + ",\n"
65 output_buffer += " FIRST_INTERNAL_KEYCODE = " + data["keys"][0] + ",\n"
66 output_buffer += " LAST_INTERNAL_KEYCODE = " + data["keys"][-1] + "\n"
67 output_buffer += "};\n"
68
69 return output_buffer + "\n"
70
71def add_pseudolayers(data):
72 output_buffer = ""
73
74 if len(data["pseudolayers"]) == 0:
75 raise Exception("You didn't define any pseudolayers")
76
77 if not len([pseudolayer["name"] for pseudolayer in data["pseudolayers"]]) == len(set([pseudolayer["name"] for pseudolayer in data["pseudolayers"]])):
78 raise Exception("The pseudolayers must have unique names")
79
80 pseudolayers = data["pseudolayers"]
81 if not "ALWAYS_ON" in [layer["name"] for layer in pseudolayers]:
82 pseudolayers += [{"name": "ALWAYS_ON", "chords": []}] # the engine expects ALWAYS_ON to exist
83
84 output_buffer += "enum pseudolayers {\n"
85 output_buffer += " " + reduce(comma_separator, [layer["name"] for layer in pseudolayers]) + "\n"
86 output_buffer += "};\n"
87
88 return output_buffer + "\n"
89
90def add_layers(data):
91 output_buffer = ""
92
93 output_buffer += "const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n"
94 for layer, counter in zip(data["layers"], range(0,len(data["layers"]))):
95 if layer["type"] == "auto":
96 output_buffer += " [" + str(counter) + "] = " + data["parameters"]["layout_function_name"] + "(" + reduce(comma_separator, [key for key in data["keys"]]) + "),\n"
97 else:
98 output_buffer += " [" + str(counter) + "] = " + data["parameters"]["layout_function_name"] + "(" + reduce(comma_separator, [key for key in layer["keycodes"]]) + "),\n"
99 output_buffer += "};\n"
100 output_buffer += "size_t keymapsCount = " + str(len(data["layers"])) + ";\n"
101
102 return output_buffer + "\n"
103
104def prep_buffers(data):
105 output_buffer = ""
106
107 output_buffer += "uint8_t keycodes_buffer_array[] = {\n"
108 output_buffer += " " + reduce(comma_separator, ["0"] * len(data["keys"])) + "\n"
109 output_buffer += "};\n"
110 output_buffer += "\n"
111
112 output_buffer += "uint8_t command_buffer[] = {\n"
113 output_buffer += " " + reduce(comma_separator, ["0"] * data["parameters"]["command_max_length"]) + "\n"
114 output_buffer += "};\n"
115 output_buffer += "\n"
116
117 output_buffer += "uint16_t leader_buffer[] = {\n"
118 output_buffer += " " + reduce(comma_separator, ["0"] * data["parameters"]["leader_max_length"]) + "\n"
119 output_buffer += "};\n"
120 output_buffer += "\n"
121
122 output_buffer += "uint8_t dynamic_macro_buffer[] = {\n"
123 output_buffer += " " + reduce(comma_separator, ["0"] * data["parameters"]["dynamic_macro_max_length"]) + "\n"
124 output_buffer += "};"
125
126 return output_buffer + "\n"
127
128def parse_keyboard_specifics(data):
129 keyboard_part_0 = add_includes(data)
130 keyboard_part_0 += add_keycodes(data)
131 keyboard_part_0 += add_pseudolayers(data)
132 keyboard_part_0 += add_parameters(data)
133 keyboard_part_0 += add_layers(data)
134 keyboard_part_0 += prep_buffers(data)
135
136 return keyboard_part_0 + '\n'
137
138def parse_chords(data):
139 keyboard_part_2 = ""
140 strings = []
141 number_of_strings = 0
142 number_of_chords = 0
143
144 for pseudolayer in data["pseudolayers"]:
145 name = pseudolayer["name"]
146 for chord in pseudolayer["chords"]:
147 if chord["type"] == "chord_set":
148 keycodes = reduce(comma_separator, [word for word in chord["keycodes"]])
149 [keyboard_part_2, number_of_chords, number_of_strings, strings] = add_chord_set(name, keycodes, chord["set"], data, keyboard_part_2, number_of_chords, number_of_strings, strings)
150 if chord["type"] == "visual_array":
151 [keyboard_part_2, number_of_chords, number_of_strings, strings] = add_dictionary(name, chord["keys"], chord["dictionary"], keyboard_part_2, number_of_chords, number_of_strings, strings)
152 if chord["type"] == "visual":
153 keycodes = reduce(comma_separator, [word for word in chord["chord"]])
154 [keyboard_part_2, number_of_chords, number_of_strings, strings] = secret_chord(name, chord["keycode"], keycodes, data, keyboard_part_2, number_of_chords, number_of_strings, strings)
155 elif chord["type"] == "simple":
156 keycodes = reduce(string_sum, ["H_" + word for word in chord["chord"]])
157 [keyboard_part_2, number_of_chords, number_of_strings, strings] = add_key(name, keycodes, chord["keycode"], keyboard_part_2, number_of_chords, number_of_strings, strings)
158 keyboard_part_2 += "\n"
159
160 keyboard_part_2 += "const struct Chord* const list_of_chords[] PROGMEM = {\n"
161 keyboard_part_2 += " " + reduce(comma_separator, ["&chord_" + str(i) for i in range(0, number_of_chords)]) + "\n"
162 keyboard_part_2 += "};\n"
163 keyboard_part_2 += "\n"
164
165 if len(data["leader_sequences"]) > 0:
166 keyboard_part_2 += reduce(newline_separator, [sequence["function"] for sequence in data["leader_sequences"]]) + "\n\n"
167 keyboard_part_2 += "const uint16_t leader_triggers[][LEADER_MAX_LENGTH] PROGMEM = {\n"
168 for sequence in data["leader_sequences"]:
169 keyboard_part_2 += " {" + reduce(comma_separator, sequence["sequence"] + ["0"] * (data["parameters"]["leader_max_length"] - len(sequence["sequence"]))) + "},\n"
170 keyboard_part_2 += "};\n\n"
171 keyboard_part_2 += "void (*leader_functions[]) (void) = {\n"
172 keyboard_part_2 += " " + reduce(comma_separator, ["&" + sequence["name"] for sequence in data["leader_sequences"]]) + "\n"
173 keyboard_part_2 += "};\n"
174 else:
175 keyboard_part_2 += "const uint16_t** const leader_triggers PROGMEM = NULL;\n"
176 keyboard_part_2 += "void (*leader_functions[]) (void) = {};\n"
177 keyboard_part_2 += "\n"
178
179 keyboard_part_2 += "#define NUMBER_OF_CHORDS " + str(number_of_chords) + "\n"
180 keyboard_part_2 += "#define NUMBER_OF_LEADER_COMBOS " + str(len(data["leader_sequences"]))
181
182 return keyboard_part_2 + "\n\n"
183
184def parse_strings_for_chords(data):
185 keyboard_part_1 = ""
186
187 for string, i in zip(strings, range(0, len(strings))):
188 keyboard_part_1 += "const char string_" + str(i) + " [] PROGMEM = \"" + string + "\";\n"
189
190 keyboard_part_1 += "\n"
191 keyboard_part_1 += "const char * const strings[] PROGMEM = {\n"
192 if len(strings) > 0:
193 keyboard_part_1 += " " + reduce(comma_separator, ["string_" + str(i) for i in range(0, len(strings))])
194 keyboard_part_1 += "\n};\n"
195
196 return keyboard_part_1
197
198def main():
199 if len(sys.argv) != 3:
200 raise Exception("Wrong number of arguments.\n\nUsage: python parser.py keymap.json keymap.c")
201
202 input_filepath = sys.argv[1]
203 output_filepath = sys.argv[2]
204
205 with open(input_filepath, "r") as read_file:
206 data = json.load(read_file)
207
208 keyboard_part_0 = parse_keyboard_specifics(data)
209 keyboard_part_1 = parse_strings_for_chords(data)
210 keyboard_part_2 = parse_chords(data)
211
212 engine_part_1 = open("engine.part.1", "r").read()
213 engine_part_2 = open("engine.part.2", "r").read() + "\n"
214 engine_part_3 = open("engine.part.3", "r").read()
215
216 output_buffer = keyboard_part_0
217 output_buffer += engine_part_1
218
219 if len(data["extra_code"]) > 0:
220 output_buffer += data["extra_code"] + "\n"
221
222 output_buffer += keyboard_part_1
223 output_buffer += engine_part_2
224 output_buffer += keyboard_part_2
225 output_buffer += engine_part_3
226
227 with open(output_filepath, "w") as write_file:
228 write_file.write(output_buffer)
229
230if __name__ == "__main__":
231 main() \ No newline at end of file