539aad89f4
also: - improved the documentation for table and array - fixed documentation font on mobile
210 lines
7.8 KiB
Python
210 lines
7.8 KiB
Python
#!/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 <mark.gillard@outlook.com.au>
|
|
# 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
|
|
from io import StringIO
|
|
|
|
class Preprocessor:
|
|
|
|
__re_strip_blocks = re.compile(r'//[#!]\s*[{][{].*?//[#!]\s*[}][}]*?\n', re.I | re.S)
|
|
__re_includes = re.compile(r'^\s*#\s*include\s+"(.+?)"', re.I | re.M)
|
|
|
|
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 = text.replace('\r\n', '\n') # convert windows newlines
|
|
text = self.__re_strip_blocks.sub('', text, 0) # strip {{ }} blocks
|
|
self.__current_level += 1
|
|
text = self.__re_includes.sub(lambda m : self.__preprocess(m), text, 0)
|
|
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 __init__(self, file):
|
|
self.__processed_includes = []
|
|
self.__header_indent = 0
|
|
self.__current_level = 0
|
|
self.__string = self.__preprocess(file)
|
|
|
|
def __str__(self):
|
|
return self.__string
|
|
|
|
|
|
|
|
def main():
|
|
|
|
# preprocess header(s)
|
|
source_text = str(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'
|
|
|
|
# change TOML_LIB_SINGLE_HEADER to 1
|
|
source_text = re.sub(
|
|
'#\s*define\s+TOML_LIB_SINGLE_HEADER\s+[0-9]+',
|
|
'#define TOML_LIB_SINGLE_HEADER 1',
|
|
source_text, 0, re.I
|
|
)
|
|
|
|
# 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
|
|
with StringIO(newline='\n') as output:
|
|
|
|
# build in a string buffer
|
|
write = lambda txt, end='\n': print(txt, file=output, end=end)
|
|
if (len(preamble) > 0):
|
|
write(utils.make_divider())
|
|
for pre in preamble:
|
|
write('//')
|
|
for line in pre.strip().splitlines():
|
|
if len(line) == 0:
|
|
write('//')
|
|
continue
|
|
if not line.startswith('//'):
|
|
write('// ', end = '')
|
|
write(line)
|
|
write('//')
|
|
write(utils.make_divider())
|
|
write(source_text)
|
|
write('')
|
|
|
|
output_str = output.getvalue().strip()
|
|
|
|
# analyze the output to find any potentially missing #undefs
|
|
re_define = re.compile(r'^\s*#\s*define\s+([a-zA-Z0-9_]+)(?:$|\s|\()')
|
|
re_undef = re.compile(r'^\s*#\s*undef\s+([a-zA-Z0-9_]+)(?:$|\s|//)')
|
|
defines = dict()
|
|
for output_line in output_str.splitlines():
|
|
defined = True
|
|
m = re_define.match(output_line)
|
|
if not m:
|
|
defined = False
|
|
m = re_undef.match(output_line)
|
|
if m:
|
|
defines[m.group(1)] = defined
|
|
ignore_list = ( # macros that are meant to stay public (user configs etc)
|
|
'INCLUDE_TOMLPLUSPLUS_H',
|
|
'TOML_API',
|
|
'TOML_UNRELEASED_FEATURES',
|
|
'TOML_LARGE_FILES',
|
|
'TOML_PARSER',
|
|
'TOML_WINDOWS_COMPAT',
|
|
'TOML_EXCEPTIONS',
|
|
'TOML_LIB_SINGLE_HEADER',
|
|
'TOML_LIB_MAJOR',
|
|
'TOML_LIB_MINOR',
|
|
'TOML_LIB_PATCH',
|
|
'TOML_LANG_MAJOR',
|
|
'TOML_LANG_MINOR',
|
|
'TOML_LANG_PATCH',
|
|
'TOML_UNDEF_MACROS',
|
|
'TOML_HEADER_ONLY',
|
|
'TOML_ALL_INLINE'
|
|
)
|
|
set_defines = []
|
|
for define, currently_set in defines.items():
|
|
if currently_set and define not in ignore_list:
|
|
set_defines.append(define)
|
|
if len(set_defines) > 0:
|
|
set_defines.sort()
|
|
print(f"Potentially missing #undefs:")
|
|
for define in set_defines:
|
|
print(f"\t#undef {define}")
|
|
|
|
# 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:
|
|
print(output_str, file=output_file)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
utils.run(main)
|