| 111 | # scope.set('item2', '%f', '4.2') # generates code that outputs ',\n"item2": 4.2' |
| 112 | # # once the scope is exited, it generates code that outputs the end of the JSON object '\n}' |
| 113 | class Scope: |
| 114 | def __init__(self, code: list[str]): |
| 115 | self.code = code |
| 116 | self.has_data = False |
| 117 | |
| 118 | def __enter__(self): |
| 119 | self.code.append('puts("{");') |
| 120 | return self |
| 121 | |
| 122 | def __exit__(self, _exc_type, _exc_val, _exc_tb): |
| 123 | if self.has_data: |
| 124 | self.code.append('puts("");') |
| 125 | self.code.append('printf("}");') |
| 126 | |
| 127 | def _start_child(self, name: str): |
| 128 | if self.has_data: |
| 129 | self.code.append('puts(",");') |
| 130 | else: |
| 131 | self.has_data = True |
| 132 | if '::' in name: |
| 133 | name = name.split('::', 1)[1] |
| 134 | self.code.append(fr'printf("\"{name}\": ");') |
| 135 | |
| 136 | def child(self, name: str): |
| 137 | self._start_child(name) |
| 138 | return Scope(self.code) |
| 139 | |
| 140 | def set(self, name: str, type_: str, value: str): |
| 141 | self._start_child(name) |
| 142 | |
| 143 | assert type_.startswith('%') |
| 144 | # We only support numeric defines as they are directly compatible with JSON. |
| 145 | # Extend to string escaping if we ever need that in the future. |
| 146 | assert type_[-1] in {'d', 'i', 'u', 'f', 'F', 'e', 'E'} |
| 147 | |
| 148 | self.code.append(f'printf("{type_}", {value});') |
| 149 | |
| 150 | def gen_inspect_code(self, path: list[str], struct: list[str | dict]): |
| 151 | if path[0][-1] == '#': |
| 152 | path[0] = path[0].rstrip('#') |
| 153 | prefix = '' |
| 154 | else: |
| 155 | prefix = 'struct ' |
| 156 | prefix += path[0] |
| 157 | |
| 158 | with self.child(path[-1]) as scope: |
| 159 | path_for_sizeof = [f'({prefix}){{}}', *path[1:]] |
| 160 | scope.set('__size__', '%zu', f'sizeof ({".".join(path_for_sizeof)})') |
| 161 | |
| 162 | for field in struct: |
| 163 | if isinstance(field, dict): |
| 164 | # We have to recurse to inspect the nested dict. |
| 165 | fname = list(field.keys())[0] |
| 166 | self.gen_inspect_code([*path, fname], field[fname]) |
| 167 | else: |
| 168 | member = ".".join([*path[1:], field]) |
| 169 | scope.set(field, '%zu', f'offsetof({prefix}, {member})') |
| 170 |
no outgoing calls
no test coverage detected