diff options
Diffstat (limited to 'lib/python/qmk/c_parse.py')
| -rw-r--r-- | lib/python/qmk/c_parse.py | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/lib/python/qmk/c_parse.py b/lib/python/qmk/c_parse.py index 72be690019..359aaccbbc 100644 --- a/lib/python/qmk/c_parse.py +++ b/lib/python/qmk/c_parse.py | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | """Functions for working with config.h files. | 1 | """Functions for working with config.h files. |
| 2 | """ | 2 | """ |
| 3 | from pygments.lexers.c_cpp import CLexer | ||
| 4 | from pygments.token import Token | ||
| 5 | from pygments import lex | ||
| 6 | from itertools import islice | ||
| 3 | from pathlib import Path | 7 | from pathlib import Path |
| 4 | import re | 8 | import re |
| 5 | 9 | ||
| @@ -13,6 +17,13 @@ multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE) | |||
| 13 | layout_macro_define_regex = re.compile(r'^#\s*define') | 17 | layout_macro_define_regex = re.compile(r'^#\s*define') |
| 14 | 18 | ||
| 15 | 19 | ||
| 20 | def _get_chunks(it, size): | ||
| 21 | """Break down a collection into smaller parts | ||
| 22 | """ | ||
| 23 | it = iter(it) | ||
| 24 | return iter(lambda: tuple(islice(it, size)), ()) | ||
| 25 | |||
| 26 | |||
| 16 | def strip_line_comment(string): | 27 | def strip_line_comment(string): |
| 17 | """Removes comments from a single line string. | 28 | """Removes comments from a single line string. |
| 18 | """ | 29 | """ |
| @@ -170,3 +181,110 @@ def _parse_matrix_locations(matrix, file, macro_name): | |||
| 170 | matrix_locations[identifier] = [row_num, col_num] | 181 | matrix_locations[identifier] = [row_num, col_num] |
| 171 | 182 | ||
| 172 | return matrix_locations | 183 | return matrix_locations |
| 184 | |||
| 185 | |||
| 186 | def _coerce_led_token(_type, value): | ||
| 187 | """ Convert token to valid info.json content | ||
| 188 | """ | ||
| 189 | value_map = { | ||
| 190 | 'NO_LED': None, | ||
| 191 | 'LED_FLAG_ALL': 0xFF, | ||
| 192 | 'LED_FLAG_NONE': 0x00, | ||
| 193 | 'LED_FLAG_MODIFIER': 0x01, | ||
| 194 | 'LED_FLAG_UNDERGLOW': 0x02, | ||
| 195 | 'LED_FLAG_KEYLIGHT': 0x04, | ||
| 196 | 'LED_FLAG_INDICATOR': 0x08, | ||
| 197 | } | ||
| 198 | if _type is Token.Literal.Number.Integer: | ||
| 199 | return int(value) | ||
| 200 | if _type is Token.Literal.Number.Float: | ||
| 201 | return float(value) | ||
| 202 | if _type is Token.Literal.Number.Hex: | ||
| 203 | return int(value, 0) | ||
| 204 | if _type is Token.Name and value in value_map.keys(): | ||
| 205 | return value_map[value] | ||
| 206 | |||
| 207 | |||
| 208 | def _parse_led_config(file, matrix_cols, matrix_rows): | ||
| 209 | """Return any 'raw' led/rgb matrix config | ||
| 210 | """ | ||
| 211 | file_contents = file.read_text(encoding='utf-8') | ||
| 212 | file_contents = comment_remover(file_contents) | ||
| 213 | file_contents = file_contents.replace('\\\n', '') | ||
| 214 | |||
| 215 | matrix_raw = [] | ||
| 216 | position_raw = [] | ||
| 217 | flags = [] | ||
| 218 | |||
| 219 | found_led_config = False | ||
| 220 | bracket_count = 0 | ||
| 221 | section = 0 | ||
| 222 | for _type, value in lex(file_contents, CLexer()): | ||
| 223 | # Assume g_led_config..stuff..; | ||
| 224 | if value == 'g_led_config': | ||
| 225 | found_led_config = True | ||
| 226 | elif value == ';': | ||
| 227 | found_led_config = False | ||
| 228 | elif found_led_config: | ||
| 229 | # Assume bracket count hints to section of config we are within | ||
| 230 | if value == '{': | ||
| 231 | bracket_count += 1 | ||
| 232 | if bracket_count == 2: | ||
| 233 | section += 1 | ||
| 234 | elif value == '}': | ||
| 235 | bracket_count -= 1 | ||
| 236 | else: | ||
| 237 | # Assume any non whitespace value here is important enough to stash | ||
| 238 | if _type in [Token.Literal.Number.Integer, Token.Literal.Number.Float, Token.Literal.Number.Hex, Token.Name]: | ||
| 239 | if section == 1 and bracket_count == 3: | ||
| 240 | matrix_raw.append(_coerce_led_token(_type, value)) | ||
| 241 | if section == 2 and bracket_count == 3: | ||
| 242 | position_raw.append(_coerce_led_token(_type, value)) | ||
| 243 | if section == 3 and bracket_count == 2: | ||
| 244 | flags.append(_coerce_led_token(_type, value)) | ||
| 245 | |||
| 246 | # Slightly better intrim format | ||
| 247 | matrix = list(_get_chunks(matrix_raw, matrix_cols)) | ||
| 248 | position = list(_get_chunks(position_raw, 2)) | ||
| 249 | matrix_indexes = list(filter(lambda x: x is not None, matrix_raw)) | ||
| 250 | |||
| 251 | # If we have not found anything - bail | ||
| 252 | if not section: | ||
| 253 | return None | ||
| 254 | |||
| 255 | # TODO: Improve crude parsing/validation | ||
| 256 | if len(matrix) != matrix_rows and len(matrix) != (matrix_rows / 2): | ||
| 257 | raise ValueError("Unable to parse g_led_config matrix data") | ||
| 258 | if len(position) != len(flags): | ||
| 259 | raise ValueError("Unable to parse g_led_config position data") | ||
| 260 | if len(matrix_indexes) and (max(matrix_indexes) >= len(flags)): | ||
| 261 | raise ValueError("OOB within g_led_config matrix data") | ||
| 262 | |||
| 263 | return (matrix, position, flags) | ||
| 264 | |||
| 265 | |||
| 266 | def find_led_config(file, matrix_cols, matrix_rows): | ||
| 267 | """Search file for led/rgb matrix config | ||
| 268 | """ | ||
| 269 | found = _parse_led_config(file, matrix_cols, matrix_rows) | ||
| 270 | if not found: | ||
| 271 | return None | ||
| 272 | |||
| 273 | # Expand collected content | ||
| 274 | (matrix, position, flags) = found | ||
| 275 | |||
| 276 | # Align to output format | ||
| 277 | led_config = [] | ||
| 278 | for index, item in enumerate(position, start=0): | ||
| 279 | led_config.append({ | ||
| 280 | 'x': item[0], | ||
| 281 | 'y': item[1], | ||
| 282 | 'flags': flags[index], | ||
| 283 | }) | ||
| 284 | for r in range(len(matrix)): | ||
| 285 | for c in range(len(matrix[r])): | ||
| 286 | index = matrix[r][c] | ||
| 287 | if index is not None: | ||
| 288 | led_config[index]['matrix'] = [r, c] | ||
| 289 | |||
| 290 | return led_config | ||
