First upload, 18 controller version

This commit is contained in:
2026-04-14 15:23:56 +02:00
commit 8c55001a1c
3810 changed files with 764061 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
from __future__ import annotations
"""Tool to dump a Python AST"""
import ast
import tokenize
from argparse import ArgumentParser, RawTextHelpFormatter
from enum import Enum
from nodedump import debug_format_node
DESCRIPTION = "Tool to dump a Python AST"
_source_lines = []
_opt_verbose = False
def first_non_space(s):
for i, c in enumerate(s):
if c != ' ':
return i
return 0
class NodeType(Enum):
IGNORE = 1
PRINT_ONE_LINE = 2 # Print as a one liner, do not visit children
PRINT = 3 # Print with opening closing tag, visit children
PRINT_WITH_SOURCE = 4 # Like PRINT, but print source line above
def get_node_type(node):
if isinstance(node, (ast.Load, ast.Store, ast.Delete)):
return NodeType.IGNORE
if isinstance(node, (ast.Add, ast.alias, ast.arg, ast.Eq, ast.Gt, ast.Lt,
ast.Mult, ast.Name, ast.NotEq, ast.NameConstant, ast.Not,
ast.Num, ast.Str)):
return NodeType.PRINT_ONE_LINE
if not hasattr(node, 'lineno'):
return NodeType.PRINT
if isinstance(node, (ast.Attribute)):
return NodeType.PRINT_ONE_LINE if isinstance(node.value, ast.Name) else NodeType.PRINT
return NodeType.PRINT_WITH_SOURCE
class DumpVisitor(ast.NodeVisitor):
def __init__(self):
ast.NodeVisitor.__init__(self)
self._indent = 0
self._printed_source_lines = {-1}
def generic_visit(self, node):
node_type = get_node_type(node)
if _opt_verbose and node_type in (NodeType.IGNORE, NodeType.PRINT_ONE_LINE):
node_type = NodeType.PRINT
if node_type == NodeType.IGNORE:
return
self._indent = self._indent + 1
indent = ' ' * self._indent
if node_type == NodeType.PRINT_WITH_SOURCE:
line_number = node.lineno - 1
if line_number not in self._printed_source_lines:
self._printed_source_lines.add(line_number)
line = _source_lines[line_number]
non_space = first_non_space(line)
print('{:04d} {}{}'.format(line_number, '_' * non_space,
line[non_space:]))
if node_type == NodeType.PRINT_ONE_LINE:
print(indent, debug_format_node(node))
else:
print(indent, '>', debug_format_node(node))
ast.NodeVisitor.generic_visit(self, node)
print(indent, '<', type(node).__name__)
self._indent = self._indent - 1
def parse_ast(filename):
node = None
with tokenize.open(filename) as f:
global _source_lines
source = f.read()
_source_lines = source.split('\n')
node = ast.parse(source, mode="exec")
return node
def create_arg_parser(desc):
parser = ArgumentParser(description=desc,
formatter_class=RawTextHelpFormatter)
parser.add_argument('--verbose', '-v', action='store_true',
help='Verbose')
parser.add_argument('source', type=str, help='Python source')
return parser
if __name__ == '__main__':
arg_parser = create_arg_parser(DESCRIPTION)
options = arg_parser.parse_args()
_opt_verbose = options.verbose
title = f'AST tree for {options.source}'
print('=' * len(title))
print(title)
print('=' * len(title))
tree = parse_ast(options.source)
DumpVisitor().visit(tree)

View File

@@ -0,0 +1,266 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
from __future__ import annotations
"""C++ formatting helper functions and formatter class"""
import ast
from .qt import ClassFlag, qt_class_flags
CLOSING = {"{": "}", "(": ")", "[": "]"} # Closing parenthesis for C++
def _fix_function_argument_type(type, for_return):
"""Fix function argument/return qualifiers using some heuristics for Qt."""
if type == "float":
return "double"
if type == "str":
type = "QString"
if not type.startswith("Q"):
return type
flags = qt_class_flags(type)
if flags & ClassFlag.PASS_BY_VALUE:
return type
if flags & ClassFlag.PASS_BY_CONSTREF:
return type if for_return else f"const {type} &"
if flags & ClassFlag.PASS_BY_REF:
return type if for_return else f"{type} &"
return type + " *" # Assume pointer by default
def to_string(node):
"""Helper to retrieve a string from the (Lists of)Name/Attribute
aggregated into some nodes"""
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
return node.attr
return ''
def format_inheritance(class_def_node):
"""Returns inheritance specification of a class"""
result = ''
for base in class_def_node.bases:
name = to_string(base)
if name != 'object':
result += ', public ' if result else ' : public '
result += name
return result
def format_for_target(target_node):
if isinstance(target_node, ast.Tuple): # for i,e in enumerate()
result = ''
for i, el in enumerate(target_node.elts):
if i > 0:
result += ', '
result += format_reference(el)
return result
return format_reference(target_node)
def format_for_loop(f_node):
"""Format a for loop
This applies some heuristics to detect:
1) "for a in [1,2])" -> "for (f: {1, 2}) {"
2) "for i in range(5)" -> "for (i = 0; i < 5; ++i) {"
3) "for i in range(2,5)" -> "for (i = 2; i < 5; ++i) {"
TODO: Detect other cases, maybe including enumerate().
"""
loop_vars = format_for_target(f_node.target)
result = 'for (' + loop_vars
if isinstance(f_node.iter, ast.Call):
f = format_reference(f_node.iter.func)
if f == 'range':
start = 0
end = -1
if len(f_node.iter.args) == 2:
start = format_literal(f_node.iter.args[0])
end = format_literal(f_node.iter.args[1])
elif len(f_node.iter.args) == 1:
end = format_literal(f_node.iter.args[0])
result += f' = {start}; {loop_vars} < {end}; ++{loop_vars}'
elif isinstance(f_node.iter, ast.List):
# Range based for over list
result += ': ' + format_literal_list(f_node.iter)
elif isinstance(f_node.iter, ast.Name):
# Range based for over variable
result += ': ' + f_node.iter.id
result += ') {'
return result
def format_name_constant(node):
"""Format a ast.NameConstant."""
if node.value is None:
return "nullptr"
return "true" if node.value else "false"
def format_literal(node):
"""Returns the value of number/string literals"""
if isinstance(node, ast.NameConstant):
return format_name_constant(node)
if isinstance(node, ast.Num):
return str(node.n)
if isinstance(node, ast.Str):
# Fixme: escaping
return f'"{node.s}"'
return ''
def format_literal_list(l_node, enclosing='{'):
"""Formats a list/tuple of number/string literals as C++ initializer list"""
result = enclosing
for i, el in enumerate(l_node.elts):
if i > 0:
result += ', '
result += format_literal(el)
result += CLOSING[enclosing]
return result
def format_member(attrib_node, qualifier_in='auto'):
"""Member access foo->member() is expressed as an attribute with
further nested Attributes/Names as value"""
n = attrib_node
result = ''
# Black magic: Guess '::' if name appears to be a class name
qualifier = qualifier_in
if qualifier_in == 'auto':
qualifier = '::' if n.attr[0:1].isupper() else '->'
while isinstance(n, ast.Attribute):
result = n.attr if not result else n.attr + qualifier + result
n = n.value
if isinstance(n, ast.Name) and n.id != 'self':
if qualifier_in == 'auto' and n.id == "Qt": # Qt namespace
qualifier = "::"
result = n.id + qualifier + result
return result
def format_reference(node, qualifier='auto'):
"""Format member reference or free item"""
return node.id if isinstance(node, ast.Name) else format_member(node, qualifier)
def format_function_def_arguments(function_def_node):
"""Formats arguments of a function definition"""
# Default values is a list of the last default values, expand
# so that indexes match
argument_count = len(function_def_node.args.args)
default_values = function_def_node.args.defaults
while len(default_values) < argument_count:
default_values.insert(0, None)
result = ''
for i, a in enumerate(function_def_node.args.args):
if result:
result += ', '
if a.arg != 'self':
if a.annotation and isinstance(a.annotation, ast.Name):
result += _fix_function_argument_type(a.annotation.id, False) + ' '
result += a.arg
if default_values[i]:
result += ' = '
default_value = default_values[i]
if isinstance(default_value, ast.Attribute):
result += format_reference(default_value)
else:
result += format_literal(default_value)
return result
def format_start_function_call(call_node):
"""Format a call of a free or member function"""
return format_reference(call_node.func) + '('
def write_import(file, i_node):
"""Print an import of a Qt class as #include"""
for alias in i_node.names:
if alias.name.startswith('Q'):
file.write(f'#include <{alias.name}>\n')
def write_import_from(file, i_node):
"""Print an import from Qt classes as #include sequence"""
# "from PySide6.QtGui import QGuiApplication" or
# "from PySide6 import QtGui"
mod = i_node.module
if not mod.startswith('PySide') and not mod.startswith('PyQt'):
return
dot = mod.find('.')
qt_module = mod[dot + 1:] + '/' if dot >= 0 else ''
for i in i_node.names:
if i.name.startswith('Q'):
file.write(f'#include <{qt_module}{i.name}>\n')
class Indenter:
"""Helper for Indentation"""
def __init__(self, output_file):
self._indent_level = 0
self._indentation = ''
self._output_file = output_file
def indent_string(self, string):
"""Start a new line by a string"""
self._output_file.write(self._indentation)
self._output_file.write(string)
def indent_line(self, line):
"""Write an indented line"""
self._output_file.write(self._indentation)
self._output_file.write(line)
self._output_file.write('\n')
def INDENT(self):
"""Write indentation"""
self._output_file.write(self._indentation)
def indent(self):
"""Increase indentation level"""
self._indent_level = self._indent_level + 1
self._indentation = ' ' * self._indent_level
def dedent(self):
"""Decrease indentation level"""
self._indent_level = self._indent_level - 1
self._indentation = ' ' * self._indent_level
class CppFormatter(Indenter):
"""Provides helpers for formatting multi-line C++ constructs"""
def __init__(self, output_file):
Indenter.__init__(self, output_file)
def write_class_def(self, class_node):
"""Print a class definition with inheritance"""
self._output_file.write('\n')
inherits = format_inheritance(class_node)
self.indent_line(f'class {class_node.name}{inherits}')
self.indent_line('{')
self.indent_line('public:')
def write_function_def(self, f_node, class_context):
"""Print a function definition with arguments"""
self._output_file.write('\n')
arguments = format_function_def_arguments(f_node)
if f_node.name == '__init__' and class_context: # Constructor
name = class_context
elif f_node.name == '__del__' and class_context: # Destructor
name = '~' + class_context
else:
return_type = "void"
if f_node.returns and isinstance(f_node.returns, ast.Name):
return_type = _fix_function_argument_type(f_node.returns.id, True)
name = return_type + " " + f_node.name
self.indent_string(f'{name}({arguments})')
self._output_file.write('\n')
self.indent_line('{')

View File

@@ -0,0 +1,51 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
from __future__ import annotations
"""Helper to dump AST nodes for debugging"""
import ast
def to_string(node):
"""Helper to retrieve a string from the (Lists of )Name/Attribute
aggregated into some nodes"""
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
return node.attr
return ''
def debug_format_node(node):
"""Format AST node for debugging"""
if isinstance(node, ast.alias):
return f'alias("{node.name}")'
if isinstance(node, ast.arg):
return f'arg({node.arg})'
if isinstance(node, ast.Attribute):
if isinstance(node.value, ast.Name):
nested_name = debug_format_node(node.value)
return f'Attribute("{node.attr}", {nested_name})'
return f'Attribute("{node.attr}")'
if isinstance(node, ast.Call):
return 'Call({}({}))'.format(to_string(node.func), len(node.args))
if isinstance(node, ast.ClassDef):
base_names = [to_string(base) for base in node.bases]
bases = ': ' + ','.join(base_names) if base_names else ''
return f'ClassDef({node.name}{bases})'
if isinstance(node, ast.ImportFrom):
return f'ImportFrom("{node.module}")'
if isinstance(node, ast.FunctionDef):
arg_names = [a.arg for a in node.args.args]
return 'FunctionDef({}({}))'.format(node.name, ', '.join(arg_names))
if isinstance(node, ast.Name):
return 'Name("{}", Ctx={})'.format(node.id, type(node.ctx).__name__)
if isinstance(node, ast.NameConstant):
return f'NameConstant({node.value})'
if isinstance(node, ast.Num):
return f'Num({node.n})'
if isinstance(node, ast.Str):
return f'Str("{node.s}")'
return type(node).__name__

View File

@@ -0,0 +1,63 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
from __future__ import annotations
"""Provides some type information on Qt classes"""
from enum import Flag
class ClassFlag(Flag):
PASS_BY_CONSTREF = 1
PASS_BY_REF = 2
PASS_BY_VALUE = 4
PASS_ON_STACK_MASK = PASS_BY_CONSTREF | PASS_BY_REF | PASS_BY_VALUE
INSTANTIATE_ON_STACK = 8
_QT_CLASS_FLAGS = {
# QtCore
"QCoreApplication": ClassFlag.INSTANTIATE_ON_STACK,
"QFile": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
"QFileInfo": ClassFlag.INSTANTIATE_ON_STACK,
"QLine": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QLineF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QModelIndex": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QPoint": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
"QPointF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QRect": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QRectF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QSaveFile": ClassFlag.INSTANTIATE_ON_STACK,
"QSettings": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
"QSize": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
"QSizeF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QString": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QTextStream": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
# QtGui
"QBrush": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QColor": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
"QGradient": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QGuiApplication": ClassFlag.INSTANTIATE_ON_STACK,
"QIcon": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QPainter": ClassFlag.INSTANTIATE_ON_STACK,
"QPen": ClassFlag.INSTANTIATE_ON_STACK,
"QPixmap": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
# QtWidgets
"QApplication": ClassFlag.INSTANTIATE_ON_STACK,
"QColorDialog": ClassFlag.INSTANTIATE_ON_STACK,
"QFileDialog": ClassFlag.INSTANTIATE_ON_STACK,
"QFontDialog": ClassFlag.INSTANTIATE_ON_STACK,
"QMessageBox": ClassFlag.INSTANTIATE_ON_STACK,
# QtQml
"QQmlApplicationEngine": ClassFlag.INSTANTIATE_ON_STACK,
"QQmlComponent": ClassFlag.INSTANTIATE_ON_STACK,
"QQmlEngine": ClassFlag.INSTANTIATE_ON_STACK,
# QtQuick
"QQuickView": ClassFlag.INSTANTIATE_ON_STACK
}
def qt_class_flags(type):
f = _QT_CLASS_FLAGS.get(type)
return f if f else ClassFlag(0)

View File

@@ -0,0 +1,56 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
from __future__ import annotations
"""Tool to dump Python Tokens"""
import sys
import tokenize
def format_token(t):
r = repr(t)
if r.startswith('TokenInfo('):
r = r[10:]
pos = r.find("), line='")
if pos < 0:
pos = r.find('), line="')
if pos > 0:
r = r[:pos + 1]
return r
def first_non_space(s):
for i, c in enumerate(s):
if c != ' ':
return i
return 0
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Specify file Name")
sys.exit(1)
filename = sys.argv[1]
indent_level = 0
indent = ''
last_line_number = -1
with tokenize.open(filename) as f:
generator = tokenize.generate_tokens(f.readline)
for t in generator:
line_number = t.start[0]
if line_number != last_line_number:
code_line = t.line.rstrip()
non_space = first_non_space(code_line)
print('{:04d} {}{}'.format(line_number, '_' * non_space,
code_line[non_space:]))
last_line_number = line_number
if t.type == tokenize.INDENT:
indent_level = indent_level + 1
indent = ' ' * indent_level
elif t.type == tokenize.DEDENT:
indent_level = indent_level - 1
indent = ' ' * indent_level
else:
print(' ', indent, format_token(t))

View File

@@ -0,0 +1,443 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
from __future__ import annotations
"""AST visitor printing out C++"""
import ast
import sys
import tokenize
import warnings
from .formatter import (CppFormatter, format_for_loop, format_literal,
format_name_constant,
format_reference, write_import, write_import_from)
from .nodedump import debug_format_node
from .qt import ClassFlag, qt_class_flags
def _is_qt_constructor(assign_node):
"""Is this assignment node a plain construction of a Qt class?
'f = QFile(name)'. Returns the class_name."""
call = assign_node.value
if (isinstance(call, ast.Call) and isinstance(call.func, ast.Name)):
func = call.func.id
if func.startswith("Q"):
return func
return None
def _is_if_main(if_node):
"""Return whether an if statement is: if __name__ == '__main__' """
test = if_node.test
return (isinstance(test, ast.Compare)
and len(test.ops) == 1
and isinstance(test.ops[0], ast.Eq)
and isinstance(test.left, ast.Name)
and test.left.id == "__name__"
and len(test.comparators) == 1
and isinstance(test.comparators[0], ast.Constant)
and test.comparators[0].value == "__main__")
class ConvertVisitor(ast.NodeVisitor, CppFormatter):
"""AST visitor printing out C++
Note on implementation:
- Any visit_XXX() overridden function should call self.generic_visit(node)
to continue visiting
- When controlling the visiting manually (cf visit_Call()),
self.visit(child) needs to be called since that dispatches to
visit_XXX(). This is usually done to prevent undesired output
for example from references of calls, etc.
"""
debug = False
def __init__(self, file_name, output_file):
ast.NodeVisitor.__init__(self)
CppFormatter.__init__(self, output_file)
self._file_name = file_name
self._class_scope = [] # List of class names
self._stack = [] # nodes
self._stack_variables = [] # variables instantiated on stack
self._debug_indent = 0
@staticmethod
def create_ast(filename):
"""Create an Abstract Syntax Tree on which a visitor can be run"""
node = None
with tokenize.open(filename) as file:
node = ast.parse(file.read(), mode="exec")
return node
def generic_visit(self, node):
parent = self._stack[-1] if self._stack else None
if self.debug:
self._debug_enter(node, parent)
self._stack.append(node)
try:
super().generic_visit(node)
except Exception as e:
line_no = node.lineno if hasattr(node, 'lineno') else -1
error_message = str(e)
message = f'{self._file_name}:{line_no}: Error "{error_message}"'
warnings.warn(message)
self._output_file.write(f'\n// {error_message}\n')
del self._stack[-1]
if self.debug:
self._debug_leave(node)
def visit_Add(self, node):
self._handle_bin_op(node, "+")
def _is_augmented_assign(self):
"""Is it 'Augmented_assign' (operators +=/-=, etc)?"""
return self._stack and isinstance(self._stack[-1], ast.AugAssign)
def visit_AugAssign(self, node):
"""'Augmented_assign', Operators +=/-=, etc."""
self.INDENT()
self.generic_visit(node)
self._output_file.write("\n")
def visit_Assign(self, node):
self.INDENT()
qt_class = _is_qt_constructor(node)
on_stack = qt_class and qt_class_flags(qt_class) & ClassFlag.INSTANTIATE_ON_STACK
# Is this a free variable and not a member assignment? Instantiate
# on stack or give a type
if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
if qt_class:
if on_stack:
# "QFile f(args)"
var = node.targets[0].id
self._stack_variables.append(var)
self._output_file.write(f"{qt_class} {var}(")
self._write_function_args(node.value.args)
self._output_file.write(");\n")
return
self._output_file.write("auto *")
line_no = node.lineno if hasattr(node, 'lineno') else -1
for target in node.targets:
if isinstance(target, ast.Tuple):
w = f"{self._file_name}:{line_no}: List assignment not handled."
warnings.warn(w)
elif isinstance(target, ast.Subscript):
w = f"{self._file_name}:{line_no}: Subscript assignment not handled."
warnings.warn(w)
else:
self._output_file.write(format_reference(target))
self._output_file.write(' = ')
if qt_class and not on_stack:
self._output_file.write("new ")
self.visit(node.value)
self._output_file.write(';\n')
def visit_Attribute(self, node):
"""Format a variable reference (cf visit_Name)"""
# Default parameter (like Qt::black)?
if self._ignore_function_def_node(node):
return
self._output_file.write(format_reference(node))
def visit_BinOp(self, node):
# Parentheses are not exposed, so, every binary operation needs to
# be enclosed by ().
self._output_file.write('(')
self.generic_visit(node)
self._output_file.write(')')
def _handle_bin_op(self, node, op):
"""Handle a binary operator which can appear as 'Augmented Assign'."""
self.generic_visit(node)
full_op = f" {op}= " if self._is_augmented_assign() else f" {op} "
self._output_file.write(full_op)
def visit_BitAnd(self, node):
self._handle_bin_op(node, "&")
def visit_BitOr(self, node):
self._handle_bin_op(node, "|")
def _format_call(self, node):
# Decorator list?
if self._ignore_function_def_node(node):
return
f = node.func
if isinstance(f, ast.Name):
self._output_file.write(f.id)
else:
# Attributes denoting chained calls "a->b()->c()". Walk along in
# reverse order, recursing for other calls.
names = []
n = f
while isinstance(n, ast.Attribute):
names.insert(0, n.attr)
n = n.value
if isinstance(n, ast.Name): # Member or variable reference
if n.id != "self":
sep = "->"
if n.id in self._stack_variables:
sep = "."
elif n.id[0:1].isupper(): # Heuristics for static
sep = "::"
self._output_file.write(n.id)
self._output_file.write(sep)
elif isinstance(n, ast.Call): # A preceding call
self._format_call(n)
self._output_file.write("->")
self._output_file.write("->".join(names))
self._output_file.write('(')
self._write_function_args(node.args)
self._output_file.write(')')
def visit_Call(self, node):
self._format_call(node)
# Context manager expression?
if self._within_context_manager():
self._output_file.write(";\n")
def _write_function_args(self, args_node):
# Manually do visit(), skip the children of func
for i, arg in enumerate(args_node):
if i > 0:
self._output_file.write(', ')
self.visit(arg)
def visit_ClassDef(self, node):
# Manually do visit() to skip over base classes
# and annotations
self._class_scope.append(node.name)
self.write_class_def(node)
self.indent()
for b in node.body:
self.visit(b)
self.dedent()
self.indent_line('};')
del self._class_scope[-1]
def visit_Div(self, node):
self._handle_bin_op(node, "/")
def visit_Eq(self, node):
self.generic_visit(node)
self._output_file.write(" == ")
def visit_Expr(self, node):
self.INDENT()
self.generic_visit(node)
self._output_file.write(';\n')
def visit_Gt(self, node):
self.generic_visit(node)
self._output_file.write(" > ")
def visit_GtE(self, node):
self.generic_visit(node)
self._output_file.write(" >= ")
def visit_For(self, node):
# Manually do visit() to get the indentation right.
# TODO: what about orelse?
self.indent_line(format_for_loop(node))
self.indent()
for b in node.body:
self.visit(b)
self.dedent()
self.indent_line('}')
def visit_FunctionDef(self, node):
class_context = self._class_scope[-1] if self._class_scope else None
for decorator in node.decorator_list:
func = decorator.func # (Call)
if isinstance(func, ast.Name) and func.id == "Slot":
self._output_file.write("\npublic slots:")
self.write_function_def(node, class_context)
# Find stack variables
for arg in node.args.args:
if arg.annotation and isinstance(arg.annotation, ast.Name):
type_name = arg.annotation.id
flags = qt_class_flags(type_name)
if flags & ClassFlag.PASS_ON_STACK_MASK:
self._stack_variables.append(arg.arg)
self.indent()
self.generic_visit(node)
self.dedent()
self.indent_line('}')
self._stack_variables.clear()
def visit_If(self, node):
# Manually do visit() to get the indentation right. Note:
# elsif() is modelled as nested if.
# Check for the main function
if _is_if_main(node):
self._output_file.write("\nint main(int argc, char *argv[])\n{\n")
self.indent()
for b in node.body:
self.visit(b)
self.indent_string("return 0;\n")
self.dedent()
self._output_file.write("}\n")
return
self.indent_string('if (')
self.visit(node.test)
self._output_file.write(') {\n')
self.indent()
for b in node.body:
self.visit(b)
self.dedent()
self.indent_string('}')
if node.orelse:
self._output_file.write(' else {\n')
self.indent()
for b in node.orelse:
self.visit(b)
self.dedent()
self.indent_string('}')
self._output_file.write('\n')
def visit_Import(self, node):
write_import(self._output_file, node)
def visit_ImportFrom(self, node):
write_import_from(self._output_file, node)
def visit_List(self, node):
# Manually do visit() to get separators right
self._output_file.write('{')
for i, el in enumerate(node.elts):
if i > 0:
self._output_file.write(', ')
self.visit(el)
self._output_file.write('}')
def visit_LShift(self, node):
self.generic_visit(node)
self._output_file.write(" << ")
def visit_Lt(self, node):
self.generic_visit(node)
self._output_file.write(" < ")
def visit_LtE(self, node):
self.generic_visit(node)
self._output_file.write(" <= ")
def visit_Mult(self, node):
self._handle_bin_op(node, "*")
def _within_context_manager(self):
"""Return whether we are within a context manager (with)."""
parent = self._stack[-1] if self._stack else None
return parent and isinstance(parent, ast.withitem)
def _ignore_function_def_node(self, node):
"""Should this node be ignored within a FunctionDef."""
if not self._stack:
return False
parent = self._stack[-1]
# A type annotation or default value of an argument?
if isinstance(parent, (ast.arguments, ast.arg)):
return True
if not isinstance(parent, ast.FunctionDef):
return False
# Return type annotation or decorator call
return node == parent.returns or node in parent.decorator_list
def visit_Index(self, node):
self._output_file.write("[")
self.generic_visit(node)
self._output_file.write("]")
def visit_Name(self, node):
"""Format a variable reference (cf visit_Attribute)"""
# Skip Context manager variables, return or argument type annotation
if self._within_context_manager() or self._ignore_function_def_node(node):
return
self._output_file.write(format_reference(node))
def visit_NameConstant(self, node):
# Default parameter?
if self._ignore_function_def_node(node):
return
self.generic_visit(node)
self._output_file.write(format_name_constant(node))
def visit_Not(self, node):
self.generic_visit(node)
self._output_file.write("!")
def visit_NotEq(self, node):
self.generic_visit(node)
self._output_file.write(" != ")
def visit_Num(self, node):
self.generic_visit(node)
self._output_file.write(format_literal(node))
def visit_RShift(self, node):
self.generic_visit(node)
self._output_file.write(" >> ")
def visit_Return(self, node):
self.indent_string("return")
if node.value:
self._output_file.write(" ")
self.generic_visit(node)
self._output_file.write(";\n")
def visit_Slice(self, node):
self._output_file.write("[")
if node.lower:
self.visit(node.lower)
self._output_file.write(":")
if node.upper:
self.visit(node.upper)
self._output_file.write("]")
def visit_Str(self, node):
self.generic_visit(node)
self._output_file.write(format_literal(node))
def visit_Sub(self, node):
self._handle_bin_op(node, "-")
def visit_UnOp(self, node):
self.generic_visit(node)
def visit_With(self, node):
self.INDENT()
self._output_file.write("{ // Converted from context manager\n")
self.indent()
for item in node.items:
self.INDENT()
if item.optional_vars:
self._output_file.write(format_reference(item.optional_vars))
self._output_file.write(" = ")
self.generic_visit(node)
self.dedent()
self.INDENT()
self._output_file.write("}\n")
def _debug_enter(self, node, parent=None):
message = '{}>generic_visit({})'.format(' ' * self ._debug_indent,
debug_format_node(node))
if parent:
message += ', parent={}'.format(debug_format_node(parent))
message += '\n'
sys.stderr.write(message)
self._debug_indent += 1
def _debug_leave(self, node):
self._debug_indent -= 1
message = '{}<generic_visit({})\n'.format(' ' * self ._debug_indent,
type(node).__name__)
sys.stderr.write(message)