diff options
Diffstat (limited to '')
-rwxr-xr-x | data/lineup-parameters | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/data/lineup-parameters b/data/lineup-parameters new file mode 100755 index 0000000..aae22de --- /dev/null +++ b/data/lineup-parameters @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +# +# Copyright © 2019 Michael Catanzaro <mcatanzaro@gnome.org> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Based on original C lineup-parameters by Sébastien Wilmet <swilmet@gnome.org> +# Rewritten in Python to allow simple execution from source directory. +# +# Usage: lineup-parameters [file] +# If the file is not given, stdin is read. +# The result is printed to stdout. +# +# The restrictions: +# - The function name must be at column 0, followed by a space and an opening +# parenthesis; +# - One parameter per line; +# - A parameter must follow certain rules (see the regex in the code), but it +# doesn't accept all possibilities of the C language. +# - The opening curly brace ("{") of the function must also be at column 0. +# +# If one restriction is missing, the function declaration is not modified. +# +# Example: +# +# gboolean +# frobnitz (Frobnitz *frobnitz, +# gint magic_number, +# GError **error) +# { +# ... +# } +# +# Becomes: +# +# gboolean +# frobnitz (Frobnitz *frobnitz, +# gint magic_number, +# GError **error) +# { +# ... +# } +# +# TODO: support "..." vararg parameter + +import argparse +import re +import sys + +from typing import NamedTuple + +# https://regexr.com/ is your friend. +functionNameRegex = re.compile(r'^(\w+) ?\(') +parameterRegex = re.compile( + r'^\s*(?P<type>(const\s+)?\w+)' + r'\s+(?P<stars>\**)' + r'\s*(?P<name>\w+)' + r'\s*(?P<end>,|\))' + r'\s*$') +openingCurlyBraceRegex = re.compile(r'^{\s*$') + + +def matchFunctionName(line): + match = functionNameRegex.match(line) + if match: + functionName = match.group(1) + firstParamPosition = match.end(0) + return (functionName, firstParamPosition) + return (None, 0) + + +class ParameterInfo(NamedTuple): + paramType: str + name: str + numStars: int + isLastParameter: bool + + +def matchParameter(line): + _, firstParamPosition = matchFunctionName(line) + match = parameterRegex.match(line[firstParamPosition:]) + if match is None: + return None + paramType = match.group('type') + assert(paramType is not None) + name = match.group('name') + assert(name is not None) + stars = match.group('stars') + numStars = len(stars) if stars is not None else 0 + end = match.group('end') + isLastParameter = True if end is not None and end == ')' else False + return ParameterInfo(paramType, name, numStars, isLastParameter) + + +def matchOpeningCurlyBrace(line): + return True if openingCurlyBraceRegex.match(line) is not None else False + + +# Length returned is number of lines the declaration takes up +def getFunctionDeclarationLength(remainingLines): + for i in range(len(remainingLines)): + currentLine = remainingLines[i] + parameterInfo = matchParameter(currentLine) + if parameterInfo is None: + return 0 + if parameterInfo.isLastParameter: + if i + 1 == len(remainingLines): + return 0 + nextLine = remainingLines[i + 1] + if not matchOpeningCurlyBrace(nextLine): + return 0 + return i + 1 + return 0 + + +def getParameterInfos(remainingLines, length): + parameterInfos = [] + for i in range(length): + parameterInfos.append(matchParameter(remainingLines[i])) + return parameterInfos + + +def computeSpacing(parameterInfos): + maxTypeLength = 0 + maxStarsLength = 0 + for parameterInfo in parameterInfos: + maxTypeLength = max(maxTypeLength, len(parameterInfo.paramType)) + maxStarsLength = max(maxStarsLength, parameterInfo.numStars) + return (maxTypeLength, maxStarsLength) + + +def printParameter(parameterInfo, maxTypeLength, maxStarsLength, outfile): + outfile.write(f'{parameterInfo.paramType:<{maxTypeLength + 1}}') + paramNamePaddedWithStars = f'{parameterInfo.name:*>{parameterInfo.numStars + len(parameterInfo.name)}}' + outfile.write(f'{paramNamePaddedWithStars:>{maxStarsLength + len(parameterInfo.name)}}') + + +def printFunctionDeclaration(remainingLines, length, useTabs, outfile): + functionName, _ = matchFunctionName(remainingLines[0]) + assert(functionName is not None) + outfile.write(f'{functionName} (') + numSpacesToParenthesis = len(functionName) + 2 + whitespace = '' + if useTabs: + tabs = ''.ljust(numSpacesToParenthesis // 8, '\t') + spaces = ''.ljust(numSpacesToParenthesis % 8) + whitespace = tabs + spaces + else: + whitespace = ''.ljust(numSpacesToParenthesis) + parameterInfos = getParameterInfos(remainingLines, length) + maxTypeLength, maxStarsLength = computeSpacing(parameterInfos) + numParameters = len(parameterInfos) + for i in range(numParameters): + parameterInfo = parameterInfos[i] + if i != 0: + outfile.write(whitespace) + printParameter(parameterInfo, maxTypeLength, maxStarsLength, outfile) + if i + 1 != numParameters: + outfile.write(',\n') + outfile.write(')\n') + + +def parseContents(infile, useTabs, outfile): + lines = infile.readlines() + i = 0 + while i < len(lines): + line = lines[i] + functionName, _ = matchFunctionName(line) + if functionName is None: + outfile.write(line) + i += 1 + continue + remainingLines = lines[i:] + length = getFunctionDeclarationLength(remainingLines) + if length == 0: + outfile.write(line) + i += 1 + continue + printFunctionDeclaration(remainingLines, length, useTabs, outfile) + i += length + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='Line up parameters of C functions') + parser.add_argument('infile', nargs='?', + type=argparse.FileType('r'), + default=sys.stdin, + help='input C source file') + parser.add_argument('-o', metavar='outfile', nargs='?', + type=argparse.FileType('w'), + default=sys.stdout, + help='where to output modified file') + parser.add_argument('--tabs', action='store_true', + help='whether use tab characters in the output') + args = parser.parse_args() + parseContents(args.infile, args.tabs, args.o) + args.infile.close() + args.o.close() |