示例#1
0
    def parse_Expr(self, node, file_index, function_key, indent):
        """
        Handles parsing an ast.Expr node.

        Parameters
        ----------
        node : ast.Expr
            The ast.Expr node to be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have
        """
        func_ref = self.output_files[file_index].functions[function_key]

        # Only worrying about docstrings and function calls
        # Docstrings classified as constants in ast
        if node.value.__class__ is ast.Constant:
            if type(node.value.value) is str:
                # Verify this is a docstring
                start_chars = self.raw_lines[node.value.lineno-1].strip()[0:3]
                if start_chars == '"""' or start_chars == "'''":
                    return_str = self.convert_docstring(node.value.value,
                                                        indent)
                else:
                    self.parse_unhandled(node, file_index, function_key, indent,
                                         "TODO: Constant string not used")
                    return

            else:
                self.parse_unhandled(node, file_index, function_key, indent,
                                     "TODO: Constant not used")
                return

        elif node.value.__class__ is ast.Call:
            try:
                return_str, return_type = self.parse_Call(node.value,
                                                          file_index,
                                                          function_key)

            except ppex.TranslationNotSupported as ex:
                self.parse_unhandled(node, file_index, function_key, indent,
                                     ex.reason)
                return

            return_str += ";"

        else:
            # Any other type doesn't matter as the work it does wouldn't be
            # saved
            self.parse_unhandled(node, file_index, function_key, indent,
                                 "TODO: Value not assigned or used")
            return

        func_ref.lines[node.value.lineno] = cline.CPPCodeLine(node.value.lineno,
                                                              node.value.end_lineno,
                                                              node.end_col_offset,
                                                              indent, return_str)
示例#2
0
    def parse_unhandled(self, node, file_index, function_key, indent,
                        reason="TODO: Code not directly translatable, manual port required"):
        """
        Handler for any code that cannot be properly translated.
        This will bring the code verbatim from the original python
        script and wrap it in a C++ comment and add the reason it
        wasn't translated one line above it

        Parameters
        ----------
        node : ast node
            The ast node that couldn't be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have
        reason : str
            The reason why a line of code wasn't translated
        """
        # Get a reference to the correct function to shorten code width
        func_ref = self.output_files[file_index].functions[function_key]
        func_ref.lines[node.lineno] = cline.CPPCodeLine(node.lineno,
                                                        node.lineno,
                                                        node.end_col_offset,
                                                        indent,
                                                        "/*" + self.raw_lines[node.lineno-1],
                                                        "", reason)

        # If the code spanned multiple lines, we need to pull all
        # of the lines from the original script, not just the first
        # line
        for index in range(node.lineno+1, node.end_lineno+1):
            func_ref.lines[index] = cline.CPPCodeLine(index,
                                                      index,
                                                      node.end_col_offset,
                                                      indent,
                                                      self.raw_lines[index-1])
        # Add the closing comment symbol on the last line
        func_ref.lines[node.end_lineno].code_str += "*/"
示例#3
0
    def parse_Return(self, node, file_index, function_key, indent):
        """
        Handles parsing an ast.Return node.

        Parameters
        ----------
        node : ast.Return
            The ast.Return node to be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have
        """
        func_ref = self.output_files[file_index].functions[function_key]
        if node.value is None:
            func_ref.lines[node.lineno] = cline.CPPCodeLine(node.lineno,
                                                            node.end_lineno,
                                                            node.end_col_offset,
                                                            indent, "return;")
        else:
            try:
                return_str, return_type = self.recurse_operator(node.value,
                                                                file_index,
                                                                function_key)
            except ppex.TranslationNotSupported as ex:
                self.parse_unhandled(node, file_index, function_key, indent,
                                     ex.reason)
                return

            func_ref.return_type = self.type_precedence(return_type,
                                                        func_ref.return_type)
            func_ref.lines[node.lineno] = cline.CPPCodeLine(node.lineno,
                                                            node.end_lineno,
                                                            node.end_col_offset,
                                                            indent,
                                                            "return " + return_str + ";")
示例#4
0
    def parse_Continue(self, node, file_index, function_key, indent):
        """
        Handles parsing an ast.Continue node.

        Parameters
        ----------
        node : ast.Continue
            The ast.Continue node to be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have
        """
        func_ref = self.output_files[file_index].functions[function_key]
        func_ref.lines[node.lineno] = cline.CPPCodeLine(node.lineno,
                                                        node.end_lineno,
                                                        node.end_col_offset,
                                                        indent,
                                                        "continue;")
示例#5
0
    def parse_While(self, node, file_index, function_key, indent):
        """
        Handles parsing an ast.While node. Can be called recursively to handle
        nested whiles.

        Parameters
        ----------
        node : ast.While
            The ast.While node to be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have
        """
        func_ref = self.output_files[file_index].functions[function_key]

        try:
            test_str = self.recurse_operator(node.test, file_index, function_key)[0]
        except ppex.TranslationNotSupported as ex:
            self.parse_unhandled(node, file_index, function_key, indent, ex.reason)
            return

        func_ref.lines[node.lineno] = cline.CPPCodeLine(node.lineno,
                                                        node.end_lineno,
                                                        node.end_col_offset,
                                                        indent,
                                                        "while (" + test_str + ")\n"
                                                        + indent * cline.CPPCodeLine.tab_delimiter
                                                        + "{")

        self.analyze_tree(node.body, file_index, function_key, indent + 1)

        # Closing the body of the while loop
        func_ref.lines[node.body[-1].end_lineno].code_str += "\n" \
                                                             + indent * cline.CPPCodeLine.tab_delimiter \
                                                             + "}"
示例#6
0
    def ingest_comments(self, raw_lines):
        """
        Pulls comments from the original script, converts them to C++ style comments, then puts them
        into line dictionaries of their corresponding function so they are included during the
        output phase

        Parameters
        ----------
        raw_lines : list of str
            List of strings containing the original python script line by line
        """
        # First get a dictionary with every existing line of code. That way
        # we know whether to look for an inline comment or a full line comment
        for file in self.output_files:
            all_lines_dict = {}
            for cfunction in file.functions.values():
                # Source: https://stackoverflow.com/questions/38987/how-do-i
                # -merge-two-dictionaries-in-a-single-expression-in-python
                # -taking-union-o
                all_lines_dict = {**all_lines_dict, **cfunction.lines}

            # Going through all lines in the script we are parsing
            for index in range(len(raw_lines)):
                # Line numbers count from 1 while list starts from 0, so we need to offset by 1
                if (index + 1) in all_lines_dict:
                    # Looking for inline comment
                    code_line = all_lines_dict[index + 1]
                    comment = raw_lines[index][code_line.
                                               end_char_index:].lstrip()

                    # Verify there is a comment present
                    if len(comment) > 0 and comment[0] == "#":
                        # Trim off the comment symbol as it will be changed
                        # to the C++ style comment
                        all_lines_dict[index +
                                       1].comment_str = comment[1:].lstrip()

                else:
                    # Determine which function the line belongs to
                    for function in file.functions.values():
                        if function.lineno < index + 1 < function.end_lineno:
                            line = raw_lines[index]
                            comment = line.lstrip()
                            if len(comment) > 0 and comment[0] == "#":
                                # C++ uses '//' to indicate comments instead of '#'
                                comment = line.replace("#", "//", 1)
                                function.lines[index + 1] = cline.CPPCodeLine(
                                    index + 1, index + 1, len(line), 0,
                                    comment)
                                break
                    else:
                        line = raw_lines[index]
                        comment = line.lstrip()
                        if len(comment) > 0 and comment[0] == "#":
                            # We add an extra indent on code not in a function
                            # since it will go into a function in C++
                            comment = cline.CPPCodeLine.tab_delimiter + line.replace(
                                "#", "//", 1)
                            file.functions["0"].lines[index +
                                                      1] = cline.CPPCodeLine(
                                                          index + 1, index + 1,
                                                          len(line), 0,
                                                          comment)

        # Sort function line dictionaries so output is in proper order
        for function in file.functions.values():
            sorted_lines = {}
            for line in sorted(function.lines.keys()):
                sorted_lines[line] = function.lines[line]
            function.lines = sorted_lines
示例#7
0
    def parse_Assign(self, node, file_index, function_key, indent):
        """
        Handles parsing an ast.Assign node.

        Parameters
        ----------
        node : ast.Assign
            The ast.Assign node to be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have

        Raises
        ------
        TranslationNotSupported
            If the python code cannot be directly translated
        """
        function_ref = self.output_files[file_index].functions[function_key]

        # Won't handle chained assignment
        if len(node.targets) > 1:
            self.parse_unhandled(node, file_index, function_key, indent,
                                 "TODO: Unable to translate chained assignment")
            return

        var_name = node.targets[0].id
        try:
            assign_str, assign_type = self.recurse_operator(node.value,
                                                            file_index,
                                                            function_key)
        except ppex.TranslationNotSupported as ex:
            self.parse_unhandled(node, file_index, function_key, indent,
                                 ex.reason)
            return

        # Find if name exists in context
        try:
            py_var_type = self.find_var_type(var_name,
                                             file_index,
                                             function_key)

            # Verify types aren't changing or we aren't losing precision
            if py_var_type[0] != assign_type[0] \
                    and (py_var_type[0] != "float" and assign_type[0] != "int"):
                # Can't do changing types in C++
                self.parse_unhandled(node, file_index, function_key, indent,
                                     "TODO: Refactor for C++. Variable types "
                                     "cannot change or potential loss of "
                                     "precision occurred")
                return

            else:
                code_str = var_name + " = " + str(assign_str) + ";"
                c_code_line = cline.CPPCodeLine(node.lineno, node.end_lineno,
                                                node.end_col_offset, indent,
                                                code_str)

        except ppex.VariableNotFound:
            # Declaration
            c_var = cvar.CPPVariable(var_name, node.lineno, assign_type)
            function_ref.variables[var_name] = c_var
            code_str = var_name + " = " + str(assign_str) + ";"
            c_code_line = cline.CPPCodeLine(node.lineno, node.end_lineno,
                                            node.end_col_offset, indent,
                                            code_str)

        function_ref.lines[node.lineno] = c_code_line
示例#8
0
    def parse_If(self, node, file_index, function_key, indent, if_str="if"):
        """
        Handles parsing an ast.If node. Can be called recursively to handle
        nested ifs.

        Parameters
        ----------
        node : ast.If
            The ast.If node to be translated
        file_index : int
            Index of the file to write to in the output_files list
        function_key : str
            Key used to find the correct function in the function dictionary
        indent : int
            How much indentation a line should have
        if_str : str
            Indicates whether to be an if or else if statement
        """
        func_ref = self.output_files[file_index].functions[function_key]

        # Parse conditions and add in the code to the current function
        try:
            test_str = self.recurse_operator(node.test, file_index, function_key)[0]

        except ppex.TranslationNotSupported as ex:
            self.parse_unhandled(node, file_index, function_key, indent, ex.reason)
            return

        func_ref.lines[node.lineno] = cline.CPPCodeLine(node.lineno,
                                                        node.end_lineno,
                                                        node.end_col_offset,
                                                        indent,
                                                        if_str + " (" + test_str + ")\n"
                                                        + indent*cline.CPPCodeLine.tab_delimiter
                                                        + "{")

        self.analyze_tree(node.body, file_index, function_key, indent+1)

        # Get the last code line and add the closing bracket
        func_ref.lines[node.body[-1].end_lineno].code_str += "\n" \
                                                             + indent*cline.CPPCodeLine.tab_delimiter \
                                                             + "}"

        # Looking for else if or else cases
        if len(node.orelse) == 1 and node.orelse[0].__class__ is ast.If:
            # Else if case
            self.parse_If(node.orelse[0], file_index, function_key, indent,
                          "else if")

        elif len(node.orelse) > 0:
            # Else case
            else_lineno, else_end_col_offset = self.find_else_lineno(node.orelse[0].lineno - 2)
            func_ref.lines[else_lineno] = cline.CPPCodeLine(else_lineno,
                                                            else_lineno,
                                                            else_end_col_offset,
                                                            indent,
                                                            "else\n"
                                                            + indent * cline.CPPCodeLine.tab_delimiter
                                                            + "{")

            self.analyze_tree(node.orelse, file_index, function_key, indent + 1)

            # Get the last code line and add the closing bracket
            func_ref.lines[node.orelse[-1].end_lineno].code_str += "\n" \
                                                                   + indent * cline.CPPCodeLine.tab_delimiter \
                                                                   + "}"