blob: c949f9466cf43c501b6e4c7ac05f219580173d46 [file]
#!/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()