def clean_code(code, comments=True, macros=False, pragmas=False):
if macros or pragmas:<EOL><INDENT>lines = code.split('<STR_LIT:\n>')<EOL>in_macro = False<EOL>in_pragma = False<EOL>for i in range(len(lines)):<EOL><INDENT>l = lines[i].strip()<EOL>if macros and (l.startswith('<STR_LIT:#>') and not l.startswith('<STR_LIT>') or in_macro):<EOL><INDENT>lines[i] = '<STR_LIT>'<EOL>in_macro = l.endswith('<STR_LIT:\\>')<EOL><DEDENT>if pragmas and (l.startswith('<STR_LIT>') or in_pragma):<EOL><INDENT>lines[i] = '<STR_LIT>'<EOL>in_pragma = l.endswith('<STR_LIT:\\>')<EOL><DEDENT><DEDENT>code = '<STR_LIT:\n>'.join(lines)<EOL><DEDENT>if comments:<EOL><INDENT>idx = <NUM_LIT:0><EOL>comment_start = None<EOL>while idx < len(code) - <NUM_LIT:1>:<EOL><INDENT>if comment_start is None and code[idx:idx + <NUM_LIT:2>] == '<STR_LIT>':<EOL><INDENT>end_idx = code.find('<STR_LIT:\n>', idx)<EOL>code = code[:idx] + code[end_idx:]<EOL>idx -= end_idx - idx<EOL><DEDENT>elif comment_start is None and code[idx:idx + <NUM_LIT:2>] == '<STR_LIT>':<EOL><INDENT>comment_start = idx<EOL><DEDENT>elif comment_start is not None and code[idx:idx + <NUM_LIT:2>] == '<STR_LIT>':<EOL><INDENT>code = (code[:comment_start] +<EOL>'<STR_LIT:\n>' * code[comment_start:idx].count('<STR_LIT:\n>') +<EOL>code[idx + <NUM_LIT:2>:])<EOL>idx -= idx - comment_start<EOL>comment_start = None<EOL><DEDENT>idx += <NUM_LIT:1><EOL><DEDENT><DEDENT>return code<EOL>
Naive comment and macro striping from source code

:param comments: If True, all comments are stripped from code
:param macros: If True, all macros are stripped from code
:param pragmas: If True, all pragmas are stripped from code

:return: cleaned code. Line numbers are preserved with blank lines,
and multiline comments and macros are supported. BUT comment-like
strings are (wrongfully) treated as comments.