Похоже, что c_generator.CGenerator
вызывает _generate_stmt
метод для сфероподобных структур , который добавляет ';\n'
(с отступом) к результату visit
для оператора, даже если это пустая строка.
Чтобы удалить вызовы функций, мы можем перегрузить ее как
class RemoveFuncCalls(c_generator.CGenerator):
def _generate_stmt(self, n, add_indent=False):
if isinstance(n, c_ast.FuncCall):
return ''
else:
return super()._generate_stmt(n, add_indent)
этим
void main()
{
x = 1;
}
, который выглядит так, как вы хотите.
Давайте рассмотрим случай
if (bar(42, "something"))
return;
, если нам нужно, чтобы он стал
if ()
return;
, тогда нам нужно добавить
def visit_FuncCall(self, n):
return ''
, как в OP, потому что _generate_stmt
не вызывается методом RemoveFuncCalls.visit_If
для cond
сериализации полей.
Идем дальше
Я не знаю, какой "канонический / идиоматический c способ удалить узлы / поддеревья из AST с pycparser ", но я знаю один для ast
модуля из stdlib - ast.NodeTransformer
class (который по какой-то причине отсутствует в pycparser
).
Это позволит нам избежать путаницы с сериализацией AST в str
путем переопределения методов private'i sh и изменения самого AST
from pycparser import c_ast
class NodeTransformer(c_ast.NodeVisitor):
def generic_visit(self, node):
for field, old_value in iter_fields(node):
if isinstance(old_value, list):
new_values = []
for value in old_value:
if isinstance(value, c_ast.Node):
value = self.visit(value)
if value is None:
continue
elif not isinstance(value, c_ast.Node):
new_values.extend(value)
continue
new_values.append(value)
old_value[:] = new_values
elif isinstance(old_value, c_ast.Node):
new_node = self.visit(old_value)
setattr(node, field, new_node)
return node
def iter_fields(node):
# this doesn't look pretty because `pycparser` decided to have structure
# for AST node classes different from stdlib ones
index = 0
children = node.children()
while index < len(children):
name, child = children[index]
try:
bracket_index = name.index('[')
except ValueError:
yield name, child
index += 1
else:
name = name[:bracket_index]
child = getattr(node, name)
index += len(child)
yield name, child
и для В этом случае он может быть просто разделен на подклассы
class FuncCallsRemover(NodeTransformer):
def visit_FuncCall(self, node):
return None
и использоваться как
...
ast = parser.parse(text)
v = FuncCallsRemover()
ast = v.visit(ast) # note that `NodeTransformer` returns modified AST instead of `None`
, после этого мы можем использовать неизмененный экземпляр c_generator.CGenerator
и получить тот же результат.