| #!/usr/bin/env python3 |
| # Copyright 2024 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Generates macros for encoding tokenizer argument types.""" |
| |
| import datetime |
| from pathlib import Path |
| import string |
| from typing import Iterable |
| |
| FILE_HEADER = """\ |
| // Copyright {year} The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| // AUTOGENERATED - DO NOT EDIT |
| // |
| // This file was generated by {script}. |
| // To make changes, update the script and run it to generate this file. |
| #pragma once |
| |
| // clang-format off |
| """ |
| |
| ARG_COUNT = 256 |
| BASES = 32 # Number of non-recursive macros to define |
| |
| NUM_COMMON_ARGS = 4 # Number of args passed to all macros. |
| |
| # Generate labels, with four extra for the common args. |
| LABELS = string.ascii_letters[: ARG_COUNT + NUM_COMMON_ARGS] |
| |
| MACRO = LABELS[0] |
| SEP = LABELS[1] |
| FORWARDED_ARG = LABELS[2] |
| BASE_INDEX = LABELS[3] |
| |
| COMMON_ARGS = f'{MACRO},{SEP},{FORWARDED_ARG}' |
| |
| |
| def _args(start: int, count: int) -> str: |
| return ','.join(f'{_arg(i)}' for i in range(start, start + count)) |
| |
| |
| def _arg(index: int) -> str: |
| return LABELS[index + NUM_COMMON_ARGS] |
| |
| |
| def _base_index(base: int | str, offset: int) -> str: |
| if isinstance(base, int): |
| return str(base + offset) |
| return f'_PW_AD({base},{offset})' |
| |
| |
| def _generate_add_macros() -> Iterable[str]: |
| """Generates macros for adding a base and an offset. |
| |
| Add macros are used so the macros are passed integer literals rather than |
| expressions. The minimum set of addition macros are supported. |
| """ |
| for base in range(0, ARG_COUNT, BASES): |
| for offset in range(BASES + 1): |
| yield f'#define _PW_ADD{base}_{offset} {base + offset}\n' |
| |
| |
| def generate_pw_apply_macro(max_supported_args: int) -> Iterable[str]: |
| """Generates macros to tokenize enums.""" |
| |
| yield FILE_HEADER.format( |
| script=Path(__file__).name, |
| year=datetime.date.today().year, |
| ) |
| |
| yield from _generate_add_macros() |
| yield '\n' |
| yield '#define _PW_AD(a, b) _PW_ADD_EXPAND(a, b)\n' |
| yield '#define _PW_ADD_EXPAND(a, b) _PW_ADD##a##_##b\n' |
| yield '#define _PW_I(macro, ...) macro(__VA_ARGS__)\n' |
| yield '\n' |
| |
| yield f'#define _PW_AP0({COMMON_ARGS},{BASE_INDEX},e)\n' |
| |
| # Generate recursive macros for applying a block of arguments. |
| for i in range(1, max_supported_args + 1): |
| # Define the first BASES macros without recursion. |
| if i <= BASES: |
| yield f'#define _PW_AP{i}({COMMON_ARGS},{BASE_INDEX},' |
| yield _args(0, i) |
| yield ')' |
| for j in range(i): |
| yield f'_PW_I({MACRO}, {_base_index(BASE_INDEX, j)}, ' |
| yield f'{FORWARDED_ARG}, {_arg(j)})' |
| if j < i - 1: |
| yield f'_PW_I({SEP}, {_base_index(BASE_INDEX, j)}, ' |
| yield f'{FORWARDED_ARG})' |
| else: |
| yield f'#define _PW_AP{i}({COMMON_ARGS},{BASE_INDEX},' |
| yield _args(0, BASES) |
| yield ',...)' |
| |
| # First BASES. |
| yield f'_PW_AP{BASES}({COMMON_ARGS},{BASE_INDEX},' |
| yield _args(0, BASES) |
| yield ')' |
| # Separator. |
| yield f'_PW_I({SEP}, {_base_index(BASE_INDEX, BASES - 1)}, ' |
| yield f'{FORWARDED_ARG})' |
| # Remaining. |
| yield f'_PW_AP{i - BASES}({COMMON_ARGS},' |
| yield f'{_base_index(BASE_INDEX, BASES)},__VA_ARGS__)' |
| yield '\n' |
| |
| yield '\n' |
| |
| |
| def _main() -> None: |
| header = ( |
| Path(__file__).parent.parent |
| / 'public' |
| / 'pw_preprocessor' |
| / 'internal' |
| / 'apply_macros.h' |
| ) |
| |
| with header.open('w') as fd: |
| fd.writelines(generate_pw_apply_macro(ARG_COUNT)) |
| |
| print('Generated PW_APPLY header in', header) |
| |
| |
| if __name__ == '__main__': |
| _main() |