#!/usr/bin/env python3 # This file is a part of toml++ and is subject to the the terms of the MIT license. # Copyright (c) 2019-2020 Mark Gillard # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. # SPDX-License-Identifier: MIT import sys import os.path as path import utils import re class Preprocessor: def __init__(self): pass def preprocess(self, match): raw_incl = match if isinstance(match, str) else match.group(1) incl = raw_incl.strip().lower() if incl in self.processed_includes: return '' self.processed_includes.append(incl) text = utils.read_all_text_from_file(path.join(utils.get_script_folder(), '..', 'include', 'toml++', incl)).strip() + '\n' text = re.sub('\r\n', '\n', text, 0, re.I | re.M) # convert windows newlines text = re.sub(r'//[#!]\s*[{][{].*?//[#!]\s*[}][}]*?\n', '', text, 0, re.I | re.S) # strip {{ }} blocks self.current_level += 1 text = re.sub(r'^\s*#\s*include\s+"(.+?)"', lambda m : self.preprocess(m), text, 0, re.I | re.M) self.current_level -= 1 if (self.current_level == 1): header_text = '↓ ' + raw_incl lpad = 28 + ((25 * (self.header_indent % 4)) - int((len(header_text) + 4) / 2)) self.header_indent += 1 text = '{}\n#if 1\n\n{}\n\n#endif\n{}\n'.format( utils.make_divider(header_text, lpad), text, utils.make_divider('↑ ' + raw_incl, lpad) ) return '\n\n' + text + '\n\n' # will get merged later def __call__(self, file): self.processed_includes = [] self.header_indent = 0 self.current_level = 0 return self.preprocess(file) def main(): # preprocess header(s) source_text = Preprocessor()('toml.h') source_text = re.sub(r'^\s*#\s*pragma\s+once\s*$', '', source_text, 0, re.I | re.M) # 'pragma once' source_text = re.sub(r'^\s*//\s*clang-format\s+.+?$', '', source_text, 0, re.I | re.M) # clang-format directives source_text = re.sub(r'^\s*//\s*SPDX-License-Identifier:.+?$', '', source_text, 0, re.I | re.M) # spdx source_text = re.sub('(?:(?:\n|^)[ \t]*//[/#!<]+[^\n]*)+\n', '\n', source_text, 0, re.I | re.M) # remove 'magic' comment blocks source_text = re.sub('(?://[/#!<].*?)\n', '\n', source_text, 0, re.I | re.M) # remove 'magic' comments source_text = re.sub('([^ \t])[ \t]+\n', '\\1\n', source_text, 0, re.I | re.M) # remove trailing whitespace source_text = re.sub('\n(?:[ \t]*\n[ \t]*)+\n', '\n\n', source_text, 0, re.I | re.M) # remove double newlines # source_text = re.sub( # blank lines between various preprocessor directives # '[#](endif(?:\s*//[^\n]*)?)\n{2,}[#](ifn?(?:def)?|define)', # '#\\1\n#\\2', # source_text, 0, re.I | re.M # ) return_type_pattern \ = r'(?:' \ + r'(?:\[\[nodiscard\]\]\s*)?' \ + r'(?:(?:friend|explicit|virtual|inline|const|operator)\s+)*' \ + r'(?:' \ + r'bool|int64_t|(?:const_)?iterator|double|void' \ + r'|node(?:_(?:view|of)<.+?>|)?|table|array|value(?:<.+?>)?' \ + r'|T|U|parse_(?:error|result)' \ + r')' \ + r'(?:\s*[&*]+)?' \ + r'(?:\s*[(]\s*[)])?' \ + r'\s+' \ + r')' blank_lines_between_returns_pattern = '({}[^\n]+)\n\n([ \t]*{})'.format(return_type_pattern, return_type_pattern) for i in range(0, 5): # remove blank lines between simple one-liner definitions source_text = re.sub('(using .+?;)\n\n([ \t]*using)', '\\1\n\\2', source_text, 0, re.I | re.M) source_text = re.sub( '([a-zA-Z_][a-zA-Z0-9_]*[ \t]+[a-zA-Z_][a-zA-Z0-9_]*[ \t]*;)' \ + '\n\n([ \t]*[a-zA-Z_][a-zA-Z0-9_]*[ \t]+[a-zA-Z_][a-zA-Z0-9_]*[ \t]*;)', '\\1\n\\2', source_text, 0, re.I | re.M) source_text = re.sub(blank_lines_between_returns_pattern, '\\1\n\\2', source_text, 0, re.I | re.M) source_text = source_text.strip() + '\n' # extract library version library_version = { 'major': 0, 'minor': 0, 'patch': 0 } match = re.search(r'^\s*#\s*define\s+TOML_LIB_MAJOR\s+([0-9]+)\s*$', source_text, re.I | re.M) if match is not None: library_version['major'] = match.group(1) match = re.search(r'^\s*#\s*define\s+TOML_LIB_MINOR\s+([0-9]+)\s*$', source_text, re.I | re.M) if match is not None: library_version['minor'] = match.group(1) match = re.search(r'^\s*#\s*define\s+TOML_LIB_(?:REVISION|PATCH)\s+([0-9]+)\s*$', source_text, re.I | re.M) if match is not None: library_version['patch'] = match.group(1) # build the preamble (license etc) preamble = [] preamble.append(''' toml++ v{major}.{minor}.{patch} https://github.com/marzer/tomlplusplus SPDX-License-Identifier: MIT'''.format(**library_version)) preamble.append(''' - THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - If you wish to submit a contribution to toml++, hooray and thanks! Before you crack on, please be aware that this file was assembled from a number of smaller files by a python script, and code contributions should not be made against it directly. You should instead make your changes in the relevant source file(s). The file names of the files that contributed to this header can be found at the beginnings and ends of the corresponding sections of this file.''') preamble.append(''' TOML language specifications: Latest: https://github.com/toml-lang/toml/blob/master/README.md v1.0.0-rc.1: https://toml.io/en/v1.0.0-rc.1 v0.5.0: https://toml.io/en/v0.5.0''') preamble.append(utils.read_all_text_from_file(path.join(utils.get_script_folder(), '..', 'LICENSE'))) # write the output file output_file_path = path.join(utils.get_script_folder(), '..', 'toml.hpp') print("Writing to {}".format(output_file_path)) with open(output_file_path,'w', encoding='utf-8', newline='\n') as output_file: if (len(preamble) > 0): print(utils.make_divider(), file=output_file) for pre in preamble: print('//', file=output_file) for line in pre.strip().splitlines(): print('//', file=output_file, end = '') if (len(line) > 0): print(' ', file=output_file, end = '') print(line, file=output_file) else: print('\n', file=output_file, end = '') print('//', file=output_file) print(utils.make_divider(), file=output_file) print('''// clang-format off #ifndef INCLUDE_TOMLPLUSPLUS_H #define INCLUDE_TOMLPLUSPLUS_H #define TOML_LIB_SINGLE_HEADER 1 ''', file=output_file) print(source_text, file=output_file) print(''' #endif // INCLUDE_TOMLPLUSPLUS_H // clang-format on''', file=output_file) if __name__ == '__main__': utils.run(main)