|
@@ -2,24 +2,60 @@
|
|
|
# Copyright (c) 2017-2019, The Tor Project, Inc.
|
|
|
# See LICENSE for licensing information
|
|
|
|
|
|
-# This script iterates over a list of C files. For each file, it looks at the
|
|
|
-# #if/#else C macros, and annotates them with comments explaining what they
|
|
|
-# match.
|
|
|
-#
|
|
|
-# For example, it replaces this:
|
|
|
-#
|
|
|
-# #ifdef HAVE_OCELOT
|
|
|
-# // 500 lines of ocelot code
|
|
|
-# #endif
|
|
|
-#
|
|
|
-# with this:
|
|
|
-#
|
|
|
-# #ifdef HAVE_OCELOT
|
|
|
-# // 500 lines of ocelot code
|
|
|
-# #endif /* defined(HAVE_OCELOT) */
|
|
|
-#
|
|
|
-# Note that only #else and #endif lines are annotated. Existing comments
|
|
|
-# on those lines are removed.
|
|
|
+r"""
|
|
|
+This script iterates over a list of C files. For each file, it looks at the
|
|
|
+#if/#else C macros, and annotates them with comments explaining what they
|
|
|
+match.
|
|
|
+
|
|
|
+For example, it replaces this kind of input...
|
|
|
+
|
|
|
+>>> INPUT = '''
|
|
|
+... #ifdef HAVE_OCELOT
|
|
|
+... C code here
|
|
|
+... #if MIMSY == BOROGROVE
|
|
|
+... block 1
|
|
|
+... block 1
|
|
|
+... block 1
|
|
|
+... block 1
|
|
|
+... #else
|
|
|
+... block 2
|
|
|
+... block 2
|
|
|
+... block 2
|
|
|
+... block 2
|
|
|
+... #endif
|
|
|
+... #endif
|
|
|
+... '''
|
|
|
+
|
|
|
+With this kind of output:
|
|
|
+>>> EXPECTED_OUTPUT = '''
|
|
|
+... #ifdef HAVE_OCELOT
|
|
|
+... C code here
|
|
|
+... #if MIMSY == BOROGROVE
|
|
|
+... block 1
|
|
|
+... block 1
|
|
|
+... block 1
|
|
|
+... block 1
|
|
|
+... #else /* !(MIMSY == BOROGROVE) */
|
|
|
+... block 2
|
|
|
+... block 2
|
|
|
+... block 2
|
|
|
+... block 2
|
|
|
+... #endif /* MIMSY == BOROGROVE */
|
|
|
+... #endif /* defined(HAVE_OCELOT) */
|
|
|
+... '''
|
|
|
+
|
|
|
+Here's how to use it:
|
|
|
+>>> import sys
|
|
|
+>>> if sys.version_info.major < 3: from cStringIO import StringIO
|
|
|
+>>> if sys.version_info.major >= 3: from io import StringIO
|
|
|
+
|
|
|
+>>> OUTPUT = StringIO()
|
|
|
+>>> translate(StringIO(INPUT), OUTPUT)
|
|
|
+>>> assert OUTPUT.getvalue() == EXPECTED_OUTPUT
|
|
|
+
|
|
|
+Note that only #else and #endif lines are annotated. Existing comments
|
|
|
+on those lines are removed.
|
|
|
+"""
|
|
|
|
|
|
import re
|
|
|
|
|
@@ -38,6 +74,17 @@ class Problem(Exception):
|
|
|
def close_parens_needed(expr):
|
|
|
"""Return the number of left-parentheses needed to make 'expr'
|
|
|
balanced.
|
|
|
+
|
|
|
+ >>> close_parens_needed("1+2")
|
|
|
+ 0
|
|
|
+ >>> close_parens_needed("(1 + 2)")
|
|
|
+ 0
|
|
|
+ >>> close_parens_needed("(1 + 2")
|
|
|
+ 1
|
|
|
+ >>> close_parens_needed("(1 + (2 *")
|
|
|
+ 2
|
|
|
+ >>> close_parens_needed("(1 + (2 * 3) + (4")
|
|
|
+ 2
|
|
|
"""
|
|
|
return expr.count("(") - expr.count(")")
|
|
|
|
|
@@ -47,6 +94,17 @@ def truncate_expression(expr, new_width):
|
|
|
characters long.
|
|
|
|
|
|
Try to return an expression with balanced parentheses.
|
|
|
+
|
|
|
+ >>> truncate_expression("1+2+3", 8)
|
|
|
+ '1+2+3'
|
|
|
+ >>> truncate_expression("1+2+3+4+5", 8)
|
|
|
+ '1+2+3...'
|
|
|
+ >>> truncate_expression("(1+2+3+4)", 8)
|
|
|
+ '(1+2...)'
|
|
|
+ >>> truncate_expression("(1+(2+3+4))", 8)
|
|
|
+ '(1+...)'
|
|
|
+ >>> truncate_expression("(((((((((", 8)
|
|
|
+ '((...))'
|
|
|
"""
|
|
|
if len(expr) <= new_width:
|
|
|
# The expression is already short enough.
|
|
@@ -69,14 +127,23 @@ def truncate_expression(expr, new_width):
|
|
|
return ellipsis
|
|
|
|
|
|
def commented_line(fmt, argument, maxwidth=LINE_WIDTH):
|
|
|
-
|
|
|
- """
|
|
|
+ # (This is a raw docstring so that our doctests can use \.)
|
|
|
+ r"""
|
|
|
Return fmt%argument, for use as a commented line. If the line would
|
|
|
- be longer than maxwidth, truncate argument.
|
|
|
+ be longer than maxwidth, truncate argument but try to keep its
|
|
|
+ parentheses balanced.
|
|
|
|
|
|
Requires that fmt%"..." will fit into maxwidth characters.
|
|
|
|
|
|
Requires that fmt ends with a newline.
|
|
|
+
|
|
|
+ >>> commented_line("/* %s */\n", "hello world", 32)
|
|
|
+ '/* hello world */\n'
|
|
|
+ >>> commented_line("/* %s */\n", "hello world", 15)
|
|
|
+ '/* hello... */\n'
|
|
|
+ >>> commented_line("#endif /* %s */\n", "((1+2) && defined(FOO))", 32)
|
|
|
+ '#endif /* ((1+2) && defi...) */\n'
|
|
|
+
|
|
|
"""
|
|
|
assert fmt.endswith("\n")
|
|
|
result = fmt % argument
|
|
@@ -208,6 +275,12 @@ def translate(f_in, f_out):
|
|
|
raise Problem("Missing #endif")
|
|
|
|
|
|
import sys,os
|
|
|
+
|
|
|
+if sys.argv[1] == "--self-test":
|
|
|
+ import doctest
|
|
|
+ doctest.testmod()
|
|
|
+ sys.exit(0)
|
|
|
+
|
|
|
for fn in sys.argv[1:]:
|
|
|
with open(fn+"_OUT", 'w') as output_file:
|
|
|
translate(open(fn, 'r'), output_file)
|