diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 6d581bc2..6a2870a9 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -1,6 +1,7 @@ abspath abstractmethod agg +annotatable api appendable argparse @@ -19,21 +20,30 @@ autoescape autosectionlabel backport baremetal +binop +Binops buf bytearray calcsize changelog chdir classmethod +elts Endian endian Endianness endianness +expr +idx lestarch LGTM lgtm Linux localhost +preannot +preannotations +postannot +postannotations Prm prm Serializables diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index b43bff76..7fdd1d05 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -43,3 +43,4 @@ ignore$ ^\.github/ pyproject.toml setup.py +\.json$ diff --git a/docs/conf.py b/docs/conf.py index d1bd5cd6..5fc17cf7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,6 +3,7 @@ Documentation: https://www.sphinx-doc.org/en/master/usage/configuration.html """ + # pylint: disable=invalid-name import re diff --git a/src/fprime/common/models/serialize/array_type.py b/src/fprime/common/models/serialize/array_type.py index c238f3e7..2ae07b52 100644 --- a/src/fprime/common/models/serialize/array_type.py +++ b/src/fprime/common/models/serialize/array_type.py @@ -3,6 +3,7 @@ Created on May 29, 2020 @author: jishii """ + from fprime.util.string_util import format_string_template from . import serializable_type @@ -95,9 +96,11 @@ def to_jsonable(self): "type": self.__class__.__name__, "size": self.LENGTH, "format": self.FORMAT, - "values": None - if self._val is None - else [member.to_jsonable() for member in self._val], + "values": ( + None + if self._val is None + else [member.to_jsonable() for member in self._val] + ), } def serialize(self): diff --git a/src/fprime/common/models/serialize/bool_type.py b/src/fprime/common/models/serialize/bool_type.py index 01c900e0..b66beaf8 100644 --- a/src/fprime/common/models/serialize/bool_type.py +++ b/src/fprime/common/models/serialize/bool_type.py @@ -2,6 +2,7 @@ Created on Dec 18, 2014 @author: tcanham, reder """ + import struct from .type_base import ValueType diff --git a/src/fprime/common/models/serialize/enum_type.py b/src/fprime/common/models/serialize/enum_type.py index 5b815a99..b5d1b179 100644 --- a/src/fprime/common/models/serialize/enum_type.py +++ b/src/fprime/common/models/serialize/enum_type.py @@ -2,6 +2,7 @@ Created on Dec 18, 2014 @author: tcanham, reder """ + import struct from .type_base import DictionaryType diff --git a/src/fprime/common/models/serialize/numerical_types.py b/src/fprime/common/models/serialize/numerical_types.py index 67f8892b..6d65aa13 100644 --- a/src/fprime/common/models/serialize/numerical_types.py +++ b/src/fprime/common/models/serialize/numerical_types.py @@ -6,6 +6,7 @@ @author mstarch """ + import abc import struct diff --git a/src/fprime/common/models/serialize/serializable_type.py b/src/fprime/common/models/serialize/serializable_type.py index 70c0ceab..824efc54 100644 --- a/src/fprime/common/models/serialize/serializable_type.py +++ b/src/fprime/common/models/serialize/serializable_type.py @@ -4,6 +4,7 @@ @author: tcanham """ + from fprime.util.string_util import format_string_template from . import array_type diff --git a/src/fprime/common/models/serialize/string_type.py b/src/fprime/common/models/serialize/string_type.py index 34391cbf..c7905d3a 100644 --- a/src/fprime/common/models/serialize/string_type.py +++ b/src/fprime/common/models/serialize/string_type.py @@ -4,6 +4,7 @@ @author: tcanham """ + import struct from fprime.constants import DATA_ENCODING diff --git a/src/fprime/constants.py b/src/fprime/constants.py index cc8cba66..c19d8c91 100644 --- a/src/fprime/constants.py +++ b/src/fprime/constants.py @@ -1,6 +1,7 @@ """ Provided constants for use within the fprime system. """ + # pylint: disable=W0105 """ In order to transmit data in a serialized format, string objects need to be encoded. Otherwise, diff --git a/src/fprime/cookiecutter_templates/cookiecutter-fprime-project/hooks/post_gen_project.py b/src/fprime/cookiecutter_templates/cookiecutter-fprime-project/hooks/post_gen_project.py index 95ca5d59..ee029301 100644 --- a/src/fprime/cookiecutter_templates/cookiecutter-fprime-project/hooks/post_gen_project.py +++ b/src/fprime/cookiecutter_templates/cookiecutter-fprime-project/hooks/post_gen_project.py @@ -9,6 +9,7 @@ @author thomas-bc """ + import subprocess import sys import requests diff --git a/src/fprime/fbuild/builder.py b/src/fprime/fbuild/builder.py index 7ca93987..ff9f640a 100644 --- a/src/fprime/fbuild/builder.py +++ b/src/fprime/fbuild/builder.py @@ -2,6 +2,7 @@ Supplies high-level build functions to the greater fprime helper CLI. This maps from user command space to the specific build system handler underneath. """ + import os import re from pathlib import Path diff --git a/src/fprime/fbuild/cli.py b/src/fprime/fbuild/cli.py index af70c1ea..46721e10 100644 --- a/src/fprime/fbuild/cli.py +++ b/src/fprime/fbuild/cli.py @@ -5,6 +5,7 @@ @author mstarch """ + import argparse from pathlib import Path from typing import Callable, Dict, List, Tuple diff --git a/src/fprime/fbuild/cmake.py b/src/fprime/fbuild/cmake.py index 23ea642b..f4b66b60 100644 --- a/src/fprime/fbuild/cmake.py +++ b/src/fprime/fbuild/cmake.py @@ -7,6 +7,7 @@ @author mstarch """ + # Get a cache directory for building CMakeList file, if need and remove at exit import collections import copy diff --git a/src/fprime/fbuild/gcovr.py b/src/fprime/fbuild/gcovr.py index 4e17116e..b7dee63d 100644 --- a/src/fprime/fbuild/gcovr.py +++ b/src/fprime/fbuild/gcovr.py @@ -1,4 +1,5 @@ """ fprime.fbuild.gcovr: coverage target implementations """ + import itertools import shutil import subprocess diff --git a/src/fprime/fbuild/settings.py b/src/fprime/fbuild/settings.py index 40c195a3..cc1a1fc7 100644 --- a/src/fprime/fbuild/settings.py +++ b/src/fprime/fbuild/settings.py @@ -6,6 +6,7 @@ @author mstarch """ + import configparser import os import sys diff --git a/src/fprime/fbuild/target.py b/src/fprime/fbuild/target.py index 9920b216..7a68d671 100644 --- a/src/fprime/fbuild/target.py +++ b/src/fprime/fbuild/target.py @@ -5,6 +5,7 @@ @author lestarch """ + import functools import itertools from abc import ABC, abstractmethod diff --git a/src/fprime/fbuild/target_definitions.py b/src/fprime/fbuild/target_definitions.py index 62e7b057..3d38837c 100644 --- a/src/fprime/fbuild/target_definitions.py +++ b/src/fprime/fbuild/target_definitions.py @@ -4,6 +4,7 @@ as such, each target need only be instantiated but need not be assigned to anything. """ + from .gcovr import GcovrTarget from .target import BuildSystemTarget, TargetScope from .types import BuildType diff --git a/src/fprime/fpp/common.py b/src/fprime/fpp/common.py index 2c9cc91c..4ff8dc9f 100644 --- a/src/fprime/fpp/common.py +++ b/src/fprime/fpp/common.py @@ -4,6 +4,7 @@ @author mstarch """ + import itertools import subprocess from pathlib import Path diff --git a/src/fprime/fpp/impl.py b/src/fprime/fpp/impl.py index 10e4717d..cc8d3010 100644 --- a/src/fprime/fpp/impl.py +++ b/src/fprime/fpp/impl.py @@ -5,7 +5,6 @@ @author thomas-bc """ - import argparse import os import tempfile diff --git a/src/fprime/fpp/utils/__init__.py b/src/fprime/fpp/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fprime/fpp/utils/fpp_to_json/__init__.py b/src/fprime/fpp/utils/fpp_to_json/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fprime/fpp/utils/fpp_to_json/example_visitors/example.fpp b/src/fprime/fpp/utils/fpp_to_json/example_visitors/example.fpp new file mode 100644 index 00000000..067424e5 --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/example_visitors/example.fpp @@ -0,0 +1,20 @@ +module A { + constant B = 1 + constant C = 0x44 + constant D = "hello" + + instance E: Component.A base id 0x4444 + instance F: Component.B base id 0x5555 \ + queue size 10 + + topology G { + import I + + instance E + instance F + + connections H { + E.pout -> F.pin + } + } +} \ No newline at end of file diff --git a/src/fprime/fpp/utils/fpp_to_json/example_visitors/example.fpp.ast.json b/src/fprime/fpp/utils/fpp_to_json/example_visitors/example.fpp.ast.json new file mode 100644 index 00000000..a2e15761 --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/example_visitors/example.fpp.ast.json @@ -0,0 +1,426 @@ +[ + { + "members" : [ + [ + [ + ], + { + "DefModule" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "A", + "members" : [ + [ + [ + ], + { + "DefConstant" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "B", + "value" : { + "AstNode" : { + "data" : { + "ExprLiteralInt" : { + "value" : "1" + } + }, + "id" : 258 + } + } + }, + "id" : 259 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "DefConstant" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "C", + "value" : { + "AstNode" : { + "data" : { + "ExprLiteralInt" : { + "value" : "0x44" + } + }, + "id" : 260 + } + } + }, + "id" : 261 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "DefConstant" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "D", + "value" : { + "AstNode" : { + "data" : { + "ExprLiteralString" : { + "value" : "hello" + } + }, + "id" : 262 + } + } + }, + "id" : 263 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "DefComponentInstance" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "E", + "component" : { + "AstNode" : { + "data" : { + "Qualified" : { + "qualifier" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "Component" + } + }, + "id" : 264 + } + }, + "name" : { + "AstNode" : { + "data" : "A", + "id" : 265 + } + } + } + }, + "id" : 266 + } + }, + "baseId" : { + "AstNode" : { + "data" : { + "ExprLiteralInt" : { + "value" : "0x4444" + } + }, + "id" : 267 + } + }, + "implType" : "None", + "file" : "None", + "queueSize" : "None", + "stackSize" : "None", + "priority" : "None", + "cpu" : "None", + "initSpecs" : [ + ] + }, + "id" : 268 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "DefComponentInstance" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "F", + "component" : { + "AstNode" : { + "data" : { + "Qualified" : { + "qualifier" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "Component" + } + }, + "id" : 269 + } + }, + "name" : { + "AstNode" : { + "data" : "B", + "id" : 270 + } + } + } + }, + "id" : 271 + } + }, + "baseId" : { + "AstNode" : { + "data" : { + "ExprLiteralInt" : { + "value" : "0x5555" + } + }, + "id" : 272 + } + }, + "implType" : "None", + "file" : "None", + "queueSize" : { + "Some" : { + "AstNode" : { + "data" : { + "ExprLiteralInt" : { + "value" : "10" + } + }, + "id" : 273 + } + } + }, + "stackSize" : "None", + "priority" : "None", + "cpu" : "None", + "initSpecs" : [ + ] + }, + "id" : 274 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "DefTopology" : { + "node" : { + "AstNode" : { + "data" : { + "name" : "G", + "members" : [ + [ + [ + ], + { + "SpecTopImport" : { + "node" : { + "AstNode" : { + "data" : { + "top" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "I" + } + }, + "id" : 350 + } + } + }, + "id" : 351 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "SpecCompInstance" : { + "node" : { + "AstNode" : { + "data" : { + "visibility" : { + "Public" : { + + } + }, + "instance" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "E" + } + }, + "id" : 353 + } + } + }, + "id" : 354 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "SpecCompInstance" : { + "node" : { + "AstNode" : { + "data" : { + "visibility" : { + "Public" : { + + } + }, + "instance" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "F" + } + }, + "id" : 356 + } + } + }, + "id" : 357 + } + } + } + }, + [ + ] + ], + [ + [ + ], + { + "SpecConnectionGraph" : { + "node" : { + "AstNode" : { + "data" : { + "Direct" : { + "name" : "H", + "connections" : [ + { + "fromPort" : { + "AstNode" : { + "data" : { + "componentInstance" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "E" + } + }, + "id" : 378 + } + }, + "portName" : { + "AstNode" : { + "data" : "pout", + "id" : 377 + } + } + }, + "id" : 379 + } + }, + "fromIndex" : "None", + "toPort" : { + "AstNode" : { + "data" : { + "componentInstance" : { + "AstNode" : { + "data" : { + "Unqualified" : { + "name" : "F" + } + }, + "id" : 382 + } + }, + "portName" : { + "AstNode" : { + "data" : "pin", + "id" : 381 + } + } + }, + "id" : 383 + } + }, + "toIndex" : "None" + } + ] + } + }, + "id" : 384 + } + } + } + }, + [ + ] + ] + ] + }, + "id" : 385 + } + } + } + }, + [ + ] + ] + ] + }, + "id" : 386 + } + } + } + }, + [ + ] + ] + ] + } +] diff --git a/src/fprime/fpp/utils/fpp_to_json/example_visitors/visit.py b/src/fprime/fpp/utils/fpp_to_json/example_visitors/visit.py new file mode 100644 index 00000000..9c404ec3 --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/example_visitors/visit.py @@ -0,0 +1,77 @@ +from pathlib import Path +from fprime.fpp.utils.fpp_to_json.helpers import openFppFile +import fprime.fpp.utils.fpp_to_json.visitors.json_conversion as JSONConverter +import fprime.fpp.utils.fpp_to_json.node_structs as NodeStructs + + +def walkModule(data, oldQf): + module = NodeStructs.Module(data) + module: NodeStructs.Module = JSONConverter.ModuleConverter(module).convert() + + if oldQf == "": + qf = module.name + else: + qf = oldQf + "." + module.name # qualifier + + module.qf = qf + + for member in module.members: + if "DefComponentInstance" in member[1]: + instance = NodeStructs.ComponentInst(member) + instance: NodeStructs.ComponentInst = JSONConverter.CompInstanceConverter( + instance + ).convert() + instance.qf = qf + "." + instance.name + + if "DefConstant" in member[1]: + constant = NodeStructs.Constant(member) + constant: NodeStructs.Constant = JSONConverter.ConstantConverter( + constant + ).convert() + constant.qf = qf + "." + constant.id + + # can be continued for other member types + + if "DefTopology" in member[1]: + walkTopology(member, qf) + + if "DefModule" in member[1]: + walkModule(member, qf) + + return qf + + +def walkTopology(data, module): + topology = NodeStructs.Topology(data) + topology: NodeStructs.Topology = JSONConverter.TopologyConverter(topology).convert() + + if module == "": + qf = topology.name + else: + qf = module + "." + topology.name # qualifier + + topology.qf = qf + + for member in topology.members: + if "DefTopology" in member[1]: + walkTopology(member, qf) + + if "DefModule" in member[1]: + walkModule(member, qf) + + return qf + + +def visit(): + AST = openFppFile(Path(__file__).parent / "example.fpp") + + for i in range(len(AST[0]["members"])): + if "DefModule" in AST[0]["members"][i][1]: + walkModule(AST[0]["members"][i], "") + + if "DefTopology" in AST[0]["members"][i][1]: + walkTopology(AST[0]["members"][i], "") + + +if __name__ == "__main__": + visit() diff --git a/src/fprime/fpp/utils/fpp_to_json/example_write/write.py b/src/fprime/fpp/utils/fpp_to_json/example_write/write.py new file mode 100644 index 00000000..564361d4 --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/example_write/write.py @@ -0,0 +1,41 @@ +import fprime.fpp.utils.fpp_to_json.visitors.writer as Writer +import fprime.fpp.utils.fpp_to_json.node_structs as NodeStructs + + +def write(): + # create a new fpp module + module = NodeStructs.Module("ExampleModule") + module.name = "ExampleModule" + + module = Writer.ModuleWriter(module) + print(module.open()) + + # create a new fpp constant + constant = NodeStructs.Constant("ExampleConstant") + constant.id = "ExampleConstant" + constant.value = "42" + + constant = Writer.ConstantWriter(constant) + print(constant.write()) + + # create a new fpp topology + topology = NodeStructs.Topology("ExampleTopology") + topology.name = "ExampleTopology" + + topology = Writer.TopologyWriter(topology) + print(topology.open()) + + # create a new fpp instance spec + instanceSpec = NodeStructs.InstanceSpec("ExampleInstance") + instanceSpec.name = "ExampleInstance" + + instanceSpec = Writer.InstanceSpecWriter(instanceSpec) + print(instanceSpec.write()) + + # close the module and topology + print(topology.close()) + print(module.close()) + + +if __name__ == "__main__": + write() diff --git a/src/fprime/fpp/utils/fpp_to_json/fpp_interface.py b/src/fprime/fpp/utils/fpp_to_json/fpp_interface.py new file mode 100644 index 00000000..ec39ac6b --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/fpp_interface.py @@ -0,0 +1,138 @@ +import subprocess +import os + + +def fpp_depend(cache_folder, input_file, locs_files) -> str: + """ + This function calculates the dependencies for an fpp file using fprime-util to get + the location of the build cache fpp-depend. + + Args: + input_file: The input fpp file to calculate dependencies for + locs_file: The locs.fpp file to use for dependency calculation + + Returns: + A string of dependencies for the input file + """ + + print(f"[fpp] Calculating fpp dependencies for {os.path.basename(input_file)}...") + + try: + fppDep = subprocess.run( + ["fpp-depend", input_file] + + locs_files + + [ + "-d", + f"{cache_folder}/direct.txt", + "-m", + f"{cache_folder}/missing.txt", + "-f", + f"{cache_folder}/framework.txt", + "-g", + f"{cache_folder}/generated.txt", + "-i", + f"{cache_folder}/include.txt", + "-u", + f"{cache_folder}/unittest.txt", + "-a", + ], + check=True, + stdout=subprocess.PIPE, + ) + + with open(f"{cache_folder}/stdout.txt", "w") as f: + f.write(fppDep.stdout.decode("utf-8")) + + except subprocess.CalledProcessError as e: + print(f"[ERR] fpp-depend failed with error: {e}") + return 1 + + raise Exception("fpp-depend failed") + + +def compute_simple_dependencies(locs_file, input): + print(f"[fpp] Calculating simple fpp dependencies for {os.path.basename(input)}...") + + try: + fppDep = subprocess.run( + ["fpp-depend", locs_file, input], + check=True, + stdout=subprocess.PIPE, + ) + + return fppDep.stdout.decode("utf-8") + except subprocess.CalledProcessError as e: + print(f"[ERR] fpp-depend failed with error: {e}") + return 1 + + +def fpp_to_json(input_file): + """ + This function runs fpp-to-json on an fpp file to generate a JSON AST. + + Args: + input_file: The input fpp file to run fpp-to-json on + + Returns: + None + """ + + # run fpp + print(f"[fpp] Running fpp-to-json for {os.path.basename(input_file)}...") + + cmdS = ["fpp-to-json", input_file, "-s"] + + try: + subprocess.run(cmdS, check=True, stdout=subprocess.PIPE) + except subprocess.CalledProcessError as e: + raise Exception(f"[ERR] fpp-to-json pt2 failed with error: {e}") + + +def fpp_format(input_file): + """ + This function runs fpp-format on an fpp file to format the file. + + Args: + input_file: The input fpp file to run fpp-format on + + Returns: + None + """ + + # run fpp-format + print(f"[fpp] Running fpp-format for {os.path.basename(input_file)}...") + + try: + fppFormat = subprocess.run( + ["fpp-format", input_file], check=True, stdout=subprocess.PIPE + ) + return fppFormat.stdout.decode("utf-8") + except subprocess.CalledProcessError as e: + print(f"[ERR] fpp-format failed with error: {e}") + return 1 + + +def fpp_locate_defs(input_file): + """ + This function runs fpp-locate-defs on an fpp file to locate definitions. + + Args: + input_file: The input fpp file to run fpp-locate-defs on + locs_file: The locs.fpp file used to find the base directory to base def locations + off of + """ + + print(f"[fpp] Running fpp-locate-defs for {os.path.basename(input_file)}...") + + base_dir = os.path.dirname(input_file) + + try: + fppLocateDefs = subprocess.run( + ["fpp-locate-defs", input_file, "-d", base_dir], + check=True, + stdout=subprocess.PIPE, + ) + return fppLocateDefs.stdout.decode("utf-8") + except subprocess.CalledProcessError as e: + print(f"[ERR] fpp-locate-defs failed with error: {e}") + return 1 diff --git a/src/fprime/fpp/utils/fpp_to_json/helpers.py b/src/fprime/fpp/utils/fpp_to_json/helpers.py new file mode 100644 index 00000000..6ea7918b --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/helpers.py @@ -0,0 +1,207 @@ +import os +from pathlib import Path +import shutil +import json +import fprime.fpp.utils.fpp_to_json.fpp_interface as fpp + + +def qualifier_calculator(qualifier_JSON): + """ + Calculate the qualified name for a variable, instance, etc + + Args: + qualifier_JSON: The JSON AST for the qualifier + """ + next_idx = qualifier_JSON["e"]["AstNode"]["data"] + + path = "" + + if "ExprDot" in next_idx: + path += qualifier_calculator(next_idx["ExprDot"]) + elif "ExprIdent" in next_idx: + path += next_idx["ExprIdent"]["value"] + + return path + "." + qualifier_JSON["id"]["AstNode"]["data"] + + +def value_parser(value_JSON): + """ + Parse a value from the JSON AST. This could be parsing integers, strings, arrays, etc. + + Args: + value_JSON: The JSON AST for the value + """ + checker = value_JSON["AstNode"]["data"] + + if "Unqualified" in checker: + return checker["Unqualified"]["name"] + elif "Qualified" in checker: + qf = ( + checker["Qualified"]["qualifier"]["AstNode"]["data"]["Unqualified"]["name"] + + "." + + checker["Qualified"]["name"]["AstNode"]["data"] + ) + return qf + elif "ExprIdent" in checker: + return checker["ExprIdent"]["value"] + elif "ExprDot" in checker: + return qualifier_calculator(checker["ExprDot"]) + else: + return parse_constant(value_JSON) + + +def Binops(binop): + """ + Convert binop string name to actual operator + + Args: + binop: The binop string name + """ + if binop == "Add": + return "+" + elif binop == "Sub": + return "-" + elif binop == "Mul": + return "*" + elif binop == "Div": + return "/" + + raise Exception("Invalid binop") + + +def parse_binop(constant_JSON): + """ + Parse a binary operation from the JSON AST + + Args: + constant_JSON: The JSON AST for the binary operation + """ + idx_LHS = 1 + idx_RHS = 2 + binop = "" + + constant_JSON = constant_JSON["ExprBinop"] + binop_LHS = constant_JSON[f"e{idx_LHS}"]["AstNode"]["data"] + + if "ExprBinop" in binop_LHS: + binop += parse_binop(binop_LHS) + else: + binop += value_parser(constant_JSON[f"e{idx_LHS}"]) + + return ( + binop + + " " + + Binops(list(constant_JSON["op"].keys())[0]) + + " " + + value_parser(constant_JSON[f"e{idx_RHS}"]) + ) + + +def parse_constant(constant_JSON): + """ + Parse a constant from the JSON AST + + Args: + constant_JSON: The JSON AST for the constant + """ + constant_Value_JSON = constant_JSON["AstNode"]["data"] + + if "ExprLiteralString" in constant_Value_JSON: + return '"' + constant_Value_JSON["ExprLiteralString"]["value"] + '"' + elif "ExprLiteralInt" in constant_Value_JSON: + return constant_Value_JSON["ExprLiteralInt"]["value"] + elif "ExprLiteralFloat" in constant_Value_JSON: + return constant_Value_JSON["ExprLiteralFloat"]["value"] + elif "ExprLiteralBool" in constant_Value_JSON: + return list(constant_Value_JSON["ExprLiteralBool"]["value"].keys())[0] + elif "ExprIdent" in constant_Value_JSON: + return constant_Value_JSON["ExprIdent"]["value"] + elif "ExprArray" in constant_Value_JSON: + return parse_array(constant_Value_JSON) + elif "ExprStruct" in constant_Value_JSON: + return parse_struct(constant_Value_JSON) + elif "ExprDot" in constant_Value_JSON: + return qualifier_calculator(constant_Value_JSON["ExprDot"]) + elif "ExprBinop" in constant_Value_JSON: + return parse_binop(constant_Value_JSON) + else: + raise Exception("Invalid constant type") + + +def parse_array(constant_JSON): + """ + Parse an array from the JSON AST + + Args: + constant_JSON: The JSON AST for the array + """ + arrayOpen = "[" + constant_Value_JSON = constant_JSON["ExprArray"]["elts"] + + for i in range(len(constant_Value_JSON)): + if "ExprArray" not in constant_Value_JSON[i]["AstNode"]["data"]: + arrayOpen = arrayOpen + parse_constant(constant_Value_JSON[i]) + if i is not len(constant_Value_JSON) - 1: + arrayOpen = arrayOpen + ", " + else: + arrayOpen = ( + arrayOpen + + parse_array(constant_Value_JSON[i]["AstNode"]["data"]) + + ", " + ) + + return arrayOpen + "]" + + +def parse_struct(constant_JSON): + """ + Parse a struct from the JSON AST + + Args: + constant_JSON: The JSON AST for the struct + """ + structOpen = "{\n" + + for param in constant_JSON["ExprStruct"]["members"]: + structOpen = ( + structOpen + + " " + + param["AstNode"]["data"]["name"] + + " = " + + value_parser(param["AstNode"]["data"]["value"]) + + ",\n" + ) + + return structOpen + "}" + + +def openFppFile(path): + if not os.path.isabs(path): + path = str(Path(path).resolve()) + + pathDir = os.path.dirname(path) + fileBasename = os.path.basename(path) + folderName = f"/{fileBasename.split('.')[0]}Cache" + + pathToFolder = pathDir + folderName + + if not os.path.exists(pathToFolder): + try: + os.mkdir(pathToFolder) + except OSError as e: + raise Exception("Creation of the directory %s failed" % (pathToFolder)) + + os.chdir(pathToFolder) + + if not os.path.exists("fpp-ast.json"): + fpp.fpp_to_json(path) + + # parse json + with open("fpp-ast.json", "r") as f: + AST = json.load(f) + + os.chdir(pathDir) + + shutil.rmtree(pathToFolder, ignore_errors=True) + + return AST diff --git a/src/fprime/fpp/utils/fpp_to_json/node_structs.py b/src/fprime/fpp/utils/fpp_to_json/node_structs.py new file mode 100644 index 00000000..20088fbe --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/node_structs.py @@ -0,0 +1,198 @@ +""" +This file defines the structures for the annotatable elements in the fpp AST. These +are separate from the converters itself such that it may be possible to write +converters from other languages to these Python data structures. + +If extending this utility, the first step would be to add a new class for the data +structure for any new element. Then, a converter is required to turn an input AST into +these structures. +""" + + +class Module: + """ + Attributes: + ast: The AST of the module + name: The name of the module + members: The members of the module + preannot: The preannotations of the module + postannot: The postannotations of the module + qf: The qualified name of the module + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + self.members = [] + + self.preannot = None + self.postannot = None + self.qf = None + + +class Constant: + """ + Attributes: + ast: The AST of the constant + id: The id (LHS) of the constant + value: The value of the constant + preannot: The preannotations of the constant + postannot: The postannotations of the constant + qf: The qualified name of the constant + """ + + def __init__(self, ast): + self.ast = ast + self.id = None + self.value = None + + self.preannot = None + self.postannot = None + self.qf = None + + +class InstanceSpec: + """ + Attributes: + ast: The AST of the instance spec + name: The name of the instance spec + visibility: The visibility of the instance spec (private, normal) + preannot: The preannotations of the instance spec + postannot: The postannotations of the instance spec + qf: The qualified name of the instance spec + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + self.visibility = None + + self.preannot = None + self.postannot = None + self.qf = None + + +class ComponentInst: + """ + Attributes: + ast: The AST of the component instance + name: The name of the component instance + component_name: The name of the component + base_id: The base id of the component instance + queue_size: The queue size of the component instance + stack_size: The stack size of the component instance + cpu: The cpu of the component instance + priority: The priority of the component instance + phases: The phases of the component instance + preannot: The preannotations of the component instance + postannot: The postannotations of the component instance + qf: The qualified name of the component instance + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + self.component_name = None + self.base_id = None + self.queue_size = None + self.stack_size = None + self.cpu = None + self.priority = None + self.phases = { + "configObjects": None, + "configComponents": None, + "readParameters": None, + "configConstants": None, + "tearDownComponents": None, + "startTasks": None, + "stopTasks": None, + "freeThreads": None, + } + + self.preannot = None + self.postannot = None + self.qf = None + + +class Port: + """ + Attributes: + ast: The AST of the port + name: The name of the port + type: The type of the port + preannot: The preannotations of the port + postannot: The postannotations of the port + qf: The qualified name of the port + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + self.parameters = [] + + self.preannot = None + self.postannot = None + self.qf = None + + +class TopologyImport: + """ + Attributes: + ast: The AST of the topology import + name: The name of the topology import + preannot: The preannotations of the topology import + postannot: The postannotations of the topology import + qf: The qualified name of the topology import + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + + self.preannot = None + self.postannot = None + self.qf = None + + +class ConnectionGraph: + """ + Attributes: + ast: The AST of the connection graph + name: The name of the connection graph + connections: The connections of the connection graph + type: The type of the connection graph + preannot: The preannotations of the connection graph + postannot: The postannotations of the connection graph + qf: The qualified name of the connection graph + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + self.connections = [] + self.type = None + + self.preannot = None + self.postannot = None + self.qf = None + + +class Topology: + """ + Attributes: + ast: The AST of the topology + name: The name of the topology + members: The members of the topology + preannot: The preannotations of the topology + postannot: The postannotations of the topology + qf: The qualified name of the topology + """ + + def __init__(self, ast): + self.ast = ast + self.name = None + self.members = [] + + self.preannot = None + self.postannot = None + self.qf = None diff --git a/src/fprime/fpp/utils/fpp_to_json/visitors/__init__.py b/src/fprime/fpp/utils/fpp_to_json/visitors/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fprime/fpp/utils/fpp_to_json/visitors/json_conversion.py b/src/fprime/fpp/utils/fpp_to_json/visitors/json_conversion.py new file mode 100644 index 00000000..6437c5c7 --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/visitors/json_conversion.py @@ -0,0 +1,417 @@ +import fprime.fpp.utils.fpp_to_json.node_structs as NodeStructs +import fprime.fpp.utils.fpp_to_json.helpers as Utils + + +class ModuleConverter: + """ + AST Name: DefModule + """ + + def __init__(self, module: NodeStructs.Module): + self.module_struct: NodeStructs.Module = module + + self.module_struct.preannot = module.ast[0] + self.module_JSON = module.ast[1] + self.module_struct.postannot = module.ast[2] + + def convert(self): + self.module_struct.name = self.module_JSON["DefModule"]["node"]["AstNode"][ + "data" + ]["name"] + self.module_struct.qf = self.module_struct.name + + self.module_struct.members = self.module_JSON["DefModule"]["node"]["AstNode"][ + "data" + ]["members"] + + return self.module_struct + + +class ConstantConverter: + """ + AST Name: DefConstant + """ + + def __init__(self, constant: NodeStructs.Constant): + self.constant_struct: NodeStructs.Constant = constant + + self.constant_struct.preannot = constant.ast[0] + self.constant_JSON = constant.ast[1] + self.constant_struct.postannot = constant.ast[2] + + def convert(self): + self.constant_struct.id = self.constant_JSON["DefConstant"]["node"]["AstNode"][ + "data" + ]["name"] + self.constant_struct.qf = self.constant_struct.id + + constant_Value_JSON = self.constant_JSON["DefConstant"]["node"]["AstNode"][ + "data" + ]["value"] + + self.constant_struct.value = Utils.parse_constant(constant_Value_JSON) + + return self.constant_struct + + +class InstanceSpecConverter: + """ + AST Name: SpecCompInstance + """ + + def __init__(self, instance: NodeStructs.InstanceSpec): + self.instance_struct: NodeStructs.InstanceSpec = instance + + self.instance_struct.preannot = instance.ast[0] + self.instance_JSON = instance.ast[1] + self.instance_struct.postannot = instance.ast[2] + + def convert(self): + instance_bit_1 = self.instance_JSON["SpecCompInstance"]["node"]["AstNode"][ + "data" + ]["instance"] + + self.instance_struct.name = Utils.value_parser(instance_bit_1) + self.instance_struct.qf = self.instance_struct.name + + if ( + "Public" + in self.instance_JSON["SpecCompInstance"]["node"]["AstNode"]["data"][ + "visibility" + ] + ): + self.instance_struct.visibility = "" + elif ( + "Private" + in self.instance_JSON["SpecCompInstance"]["node"]["AstNode"]["data"][ + "visibility" + ] + ): + self.instance_struct.visibility = "private" + + return self.instance_struct + + +class CompInstanceConverter: + """ + AST Name: DefComponentInstance + """ + + def __init__(self, instance: NodeStructs.ComponentInst): + self.instance_struct: NodeStructs.ComponentInst = instance + + self.instance_struct.preannot = instance.ast[0] + self.instance_JSON = instance.ast[1] + self.instance_struct.postannot = instance.ast[2] + + def convert(self): + self._parse() + self._parse_phases() + + return self.instance_struct + + def _parse_phases(self): + initSpecs = self.instance_JSON["DefComponentInstance"]["node"]["AstNode"][ + "data" + ]["initSpecs"] + + for initSpec in initSpecs: + if "phase" in initSpec[1]["AstNode"]["data"]: + specType = initSpec[1]["AstNode"]["data"]["phase"]["AstNode"]["data"][ + "ExprDot" + ]["id"]["AstNode"]["data"] + specCode = initSpec[1]["AstNode"]["data"]["code"] + + try: + self.instance_struct.phases[specType] = specCode + except KeyError: + print( + "[WARN] Phase type not found in instance JSON [InstanceParser.parse_phases]" + ) + + def _parse(self): + self.instance_struct.name = self.instance_JSON["DefComponentInstance"]["node"][ + "AstNode" + ]["data"]["name"] + self.instance_struct.qf = self.instance_struct.name + + components_bit_1 = self.instance_JSON["DefComponentInstance"]["node"][ + "AstNode" + ]["data"]["component"] + + self.instance_struct.component_name = Utils.value_parser(components_bit_1) + self.instance_struct.base_id = Utils.value_parser( + self.instance_JSON["DefComponentInstance"]["node"]["AstNode"]["data"][ + "baseId" + ] + ) + + if ( + "queueSize" + in self.instance_JSON["DefComponentInstance"]["node"]["AstNode"]["data"] + ): + self.instance_struct.queue_size = self.instance_JSON[ + "DefComponentInstance" + ]["node"]["AstNode"]["data"]["queueSize"] + if ( + "stackSize" + in self.instance_JSON["DefComponentInstance"]["node"]["AstNode"]["data"] + ): + self.instance_struct.stack_size = self.instance_JSON[ + "DefComponentInstance" + ]["node"]["AstNode"]["data"]["stackSize"] + if ( + "priority" + in self.instance_JSON["DefComponentInstance"]["node"]["AstNode"]["data"] + ): + self.instance_struct.priority = self.instance_JSON["DefComponentInstance"][ + "node" + ]["AstNode"]["data"]["priority"] + + if ( + self.instance_struct.queue_size is not None + and self.instance_struct.queue_size != "None" + ): + queueSize_JSON = self.instance_struct.queue_size["Some"]["AstNode"]["data"] + + if "ExprLiteralInt" in queueSize_JSON: + self.instance_struct.queue_size = queueSize_JSON["ExprLiteralInt"][ + "value" + ] + elif "ExprIdent" in queueSize_JSON: + self.instance_struct.queue_size = queueSize_JSON["ExprIdent"]["value"] + elif "ExprDot" in queueSize_JSON: + self.instance_struct.queue_size = Utils.qualifier_calculator( + queueSize_JSON["ExprDot"] + ) + + if ( + self.instance_struct.stack_size is not None + and self.instance_struct.stack_size != "None" + ): + stackSize_JSON = self.instance_struct.stack_size["Some"]["AstNode"]["data"] + + if "ExprLiteralInt" in stackSize_JSON: + self.instance_struct.stack_size = stackSize_JSON["ExprLiteralInt"][ + "value" + ] + elif "ExprIdent" in stackSize_JSON: + self.instance_struct.stack_size = stackSize_JSON["ExprIdent"]["value"] + elif "ExprDot" in stackSize_JSON: + self.instance_struct.stack_size = Utils.qualifier_calculator( + stackSize_JSON["ExprDot"] + ) + + if ( + self.instance_struct.priority is not None + and self.instance_struct.priority != "None" + ): + priority_JSON = self.instance_struct.priority["Some"]["AstNode"]["data"] + + if "ExprLiteralInt" in priority_JSON: + self.instance_struct.priority = priority_JSON["ExprLiteralInt"]["value"] + elif "ExprIdent" in priority_JSON: + self.instance_struct.priority = priority_JSON["ExprIdent"]["value"] + elif "ExprDot" in priority_JSON: + self.instance_struct.priority = Utils.qualifier_calculator( + priority_JSON["ExprDot"] + ) + + if ( + "cpu" + in self.instance_JSON["DefComponentInstance"]["node"]["AstNode"]["data"] + ): + self.instance_struct.cpu = self.instance_JSON["DefComponentInstance"][ + "node" + ]["AstNode"]["data"]["cpu"] + + if ( + self.instance_struct.cpu is not None + and self.instance_struct.cpu != "None" + ): + cpu_JSON = self.instance_struct.cpu["Some"]["AstNode"]["data"] + + if "ExprLiteralInt" in cpu_JSON: + self.instance_struct.cpu = cpu_JSON["ExprLiteralInt"]["value"] + elif "ExprIdent" in cpu_JSON: + self.instance_struct.cpu = cpu_JSON["ExprIdent"]["value"] + elif "ExprDot" in cpu_JSON: + self.instance_struct.cpu = Utils.qualifier_calculator( + cpu_JSON["ExprDot"] + ) + + +class PortConverter: + """ + AST Name: DefPort + """ + + def __init__(self, port: NodeStructs.Port): + self.port_struct: NodeStructs.Port = port + + self.port_struct.preannot = port.ast[0] + self.port_JSON = port.ast[1] + self.port_struct.postannot = port.ast[2] + + def convert(self): + self.port_struct.name = self.port_JSON["DefPort"]["node"]["AstNode"]["data"][ + "name" + ] + self.port_struct.qf = self.port_name + + for i in self.port_JSON["DefPort"]["node"]["AstNode"]["data"]["params"]: + param = self.port_JSON["DefPort"]["node"]["AstNode"]["data"]["params"][i][1] + paramToAppend = { + "name": None, + "type": None, + } + + paramToAppend["name"] = param["AstNode"]["data"]["name"] + paramToAppend["type"] = param["AstNode"]["data"]["typeName"] + paramToAppend["type"] = Utils.value_parser(paramToAppend["type"]) + + self.port_struct.parameters.append(paramToAppend) + + return self.port_struct + + +class TopologyImportConverter: + """ + AST Name: SpecTopImport + """ + + def __init__(self, import_struct: NodeStructs.TopologyImport): + self.import_struct = import_struct + + self.import_struct.preannot = import_struct.ast[0] + self.import_JSON = import_struct.ast[1] + self.import_struct.postannot = import_struct.ast[2] + + def convert(self): + import_bit_1 = self.import_JSON["SpecTopImport"]["node"]["AstNode"]["data"][ + "top" + ] + + self.import_struct.name = Utils.value_parser(import_bit_1) + self.import_struct.qf = self.import_struct.name + + return self.import_struct + + +class ConnectionGraphConverter: + """ + AST Name: SpecConnectionGraph + """ + + def __init__(self, cg: NodeStructs.ConnectionGraph): + self.cg_struct = cg + + self.cg_struct.preannot = cg.ast[0] + self.cg_JSON = cg.ast[1] + self.cg_struct.postannot = cg.ast[2] + + def convert(self): + import_bit = self.cg_JSON["SpecConnectionGraph"]["node"]["AstNode"]["data"] + + if "Direct" not in import_bit: + self.cg_struct.type = "Pattern" + return + else: + self.cg_struct.type = "Direct" + import_bit = import_bit["Direct"] + + self.cg_struct.name = import_bit["name"] + self.cg_struct.qf = self.cg_struct.name + + for connection in import_bit["connections"]: + connectionToAppend = { + "source": { + "name": None, + "num": None, + }, + "dest": { + "name": None, + "num": None, + }, + } + + fromPort = connection["fromPort"]["AstNode"]["data"] + compInst = fromPort["componentInstance"] + + connectionToAppend["source"]["name"] = ( + Utils.value_parser(compInst) + + "." + + fromPort["portName"]["AstNode"]["data"] + ) + + if connectionToAppend["source"]["name"][-4:] == "None": + connectionToAppend["source"]["name"] = connectionToAppend["source"][ + "name" + ].replace("None", "") + + if connection["fromIndex"] != "None": + if connection["fromIndex"] is None or connection["fromIndex"] == "None": + connectionToAppend["source"]["num"] = "" + if type(connection["fromIndex"]) is dict: + connectionToAppend["source"]["num"] = ( + "[" + Utils.value_parser(connection["fromIndex"]["Some"]) + "]" + ) + else: + connectionToAppend["source"]["num"] = ( + "[" + connection["fromIndex"] + "]" + ) + + toPort = connection["toPort"]["AstNode"]["data"] + compInst = toPort["componentInstance"] + + connectionToAppend["dest"]["name"] = ( + Utils.value_parser(compInst) + + "." + + toPort["portName"]["AstNode"]["data"] + ) + + if connectionToAppend["dest"]["name"][-4:] == "None": + connectionToAppend["dest"]["name"] = connectionToAppend["dest"][ + "name" + ].replace("None", "") + + if connection["toIndex"] != "None": + if connection["toIndex"] is None or connection["toIndex"] == "None": + connectionToAppend["dest"]["num"] = "" + if type(connection["toIndex"]) is dict: + connectionToAppend["dest"]["num"] = ( + "[" + Utils.value_parser(connection["toIndex"]["Some"]) + "]" + ) + else: + connectionToAppend["dest"]["num"] = ( + "[" + connection["toIndex"] + "]" + ) + + self.cg_struct.connections.append(connectionToAppend) + + return self.cg_struct + + +class TopologyConverter: + """ + AST Name: DefTopology + """ + + def __init__(self, topology: NodeStructs.Topology): + self.topology_struct: NodeStructs.Topology = topology + + self.topology_struct.preannot = topology.ast[0] + self.topology_JSON = topology.ast[1] + self.topology_struct.postannot = topology.ast[2] + + def convert(self): + self.topology_struct.name = self.topology_JSON["DefTopology"]["node"][ + "AstNode" + ]["data"]["name"] + self.topology_struct.qf = self.topology_struct.name + + self.topology_struct.members = self.topology_JSON["DefTopology"]["node"][ + "AstNode" + ]["data"]["members"] + + return self.topology_struct diff --git a/src/fprime/fpp/utils/fpp_to_json/visitors/writer.py b/src/fprime/fpp/utils/fpp_to_json/visitors/writer.py new file mode 100644 index 00000000..6e33004a --- /dev/null +++ b/src/fprime/fpp/utils/fpp_to_json/visitors/writer.py @@ -0,0 +1,171 @@ +import fprime.fpp.utils.fpp_to_json.node_structs as NodeStructs + + +class ModuleWriter: + """ + Writer for an fpp module. Has open() and close() functions to write the module. + """ + + def __init__(self, module: NodeStructs.Module): + self.module: NodeStructs.Module = module + + def open(self): + return f"module {self.module.name} {{" + + def close(self): + return "}" + + +class TopologyWriter: + """ + Writer for an fpp topology. Has open() and close() functions to write the topology. + """ + + def __init__(self, topology: NodeStructs.Topology): + self.topology: NodeStructs.Topology = topology + + def open(self): + return f"topology {self.topology.name} {{" + + def close(self): + return "}" + + +class ConstantWriter: + """ + Writer for an fpp constant. Has write() function to write the constant out. + """ + + def __init__(self, constant: NodeStructs.Constant): + self.constant: NodeStructs.Constant = constant + + def write(self): + return f"constant {self.constant.id} = {self.constant.value}" + + +class InstanceSpecWriter: + """ + Writer for an fpp instance spec. Has write() function to write the instance spec out. + """ + + def __init__(self, instance_spec: NodeStructs.InstanceSpec): + self.instance_spec: NodeStructs.InstanceSpec = instance_spec + + def write(self): + return f"{self.instance_spec.visibility} instance {self.instance_spec.name}" + + +class CompInstanceWriter: + """ + Writer for an fpp component instance. Has write() function to write the component + instance definition out. + """ + + def __init__(self, comp_instance: NodeStructs.ComponentInst): + self.comp_instance: NodeStructs.ComponentInst = comp_instance + + def _writePhases(self): + phaseOpen = False + part = "" + + for phase in self.comp_instance.phases: + if ( + self.comp_instance.phases[phase] is not None + and self.comp_instance.phases[phase] != "" + ): + if not phaseOpen: + part += f"{{" + phaseOpen = True + + part += f'\n phase Fpp.ToCpp.Phases.{phase} """\n' + part += self.comp_instance.phases[phase] + part += '"""' + + if phaseOpen: + part += "}" + + return part + + def write(self): + part_1 = f"instance {self.comp_instance.name}: {self.comp_instance.component_name} base id {self.comp_instance.base_id}" + + if ( + self.comp_instance.queue_size is not None + and self.comp_instance.queue_size != "None" + ): + part_1 = part_1 + f" \\ \n queue size {self.comp_instance.queue_size}" + + if ( + self.comp_instance.stack_size is not None + and self.comp_instance.stack_size != "None" + ): + part_1 = part_1 + f" \\ \n stack size {self.comp_instance.stack_size}" + + if ( + self.comp_instance.priority is not None + and self.comp_instance.priority != "None" + ): + part_1 = part_1 + f" \\ \n priority {self.comp_instance.priority}" + + if self.comp_instance.cpu is not None and self.comp_instance.cpu != "None": + part_1 = part_1 + f" \\ \n cpu {self.comp_instance.cpu}" + + return part_1 + self._writePhases() + + +class PortWriter: + """ + Writer for an fpp port. Has write() function to write the port definition out. + """ + + def __init__(self, port: NodeStructs.Port): + self.port: NodeStructs.Port = port + + def write(self): + portParams = "" + + for i in self.port.parameters: + portParams = portParams + f"{i['name']} : {i['type']},\n" + + return f"port {self.port.name}(\n {portParams}\n)" + + +class TopologyImportWriter: + """ + Writer for an fpp topology import. Has write() function to write the import out. + """ + + def __init__(self, topology_import: NodeStructs.TopologyImport): + self.topology_import: NodeStructs.TopologyImport = topology_import + + def write(self): + return f"import {self.topology_import.name}" + + +class ConnectionGraphWriter: + """ + Writer for an fpp connection graph. Has write() function to write the connection graph out. + """ + + def __init__(self, connection_graph: NodeStructs.ConnectionGraph): + self.connection_graph: NodeStructs.ConnectionGraph = connection_graph + + def write(self): + part = f"connections {self.connection_graph.name} {{" + + for connection in self.connection_graph.connections: + if ( + connection["source"]["num"] is None + or connection["source"]["num"] == "None" + ): + connection["source"]["num"] = "" + + if connection["dest"]["num"] is None or connection["dest"]["num"] == "None": + connection["dest"]["num"] = "" + + part = ( + part + + f"\n {connection['source']['name']}{connection['source']['num']} -> {connection['dest']['name']}{connection['dest']['num']}" + ) + + return part + "\n}" diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index aa5ba4e7..99409bb1 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -2,6 +2,7 @@ @author thomas-bc """ + import argparse import shutil import subprocess diff --git a/src/fprime/util/__main__.py b/src/fprime/util/__main__.py index 41892613..a62f80be 100644 --- a/src/fprime/util/__main__.py +++ b/src/fprime/util/__main__.py @@ -4,6 +4,7 @@ This acts as the main entry point for the fprime.util module. This allows users to run fprime.util as an entry point. This will include the build_helper scripts and run them. """ + import sys import fprime.util.cli diff --git a/src/fprime/util/build_helper.py b/src/fprime/util/build_helper.py index 0893e7d0..b054f639 100644 --- a/src/fprime/util/build_helper.py +++ b/src/fprime/util/build_helper.py @@ -13,6 +13,7 @@ @author mstarch """ + from pathlib import Path from fprime.fbuild.builder import Build, BuildType diff --git a/src/fprime/util/cli.py b/src/fprime/util/cli.py index e23435ed..a91052f0 100644 --- a/src/fprime/util/cli.py +++ b/src/fprime/util/cli.py @@ -4,6 +4,7 @@ @author mstarch """ + import argparse import os import re diff --git a/src/fprime/util/help_text.py b/src/fprime/util/help_text.py index ffb74c08..62cff235 100644 --- a/src/fprime/util/help_text.py +++ b/src/fprime/util/help_text.py @@ -15,6 +15,7 @@ @author lestarch """ + import os import sys diff --git a/src/fprime/util/versioning.py b/src/fprime/util/versioning.py index 68f783c8..e2aa2f2a 100644 --- a/src/fprime/util/versioning.py +++ b/src/fprime/util/versioning.py @@ -1,4 +1,5 @@ """ FPP tools to requirements file version check """ + import argparse import sys from pathlib import Path diff --git a/test/fprime/common/models/serialize/test_types.py b/test/fprime/common/models/serialize/test_types.py index a3f0c15f..e299a8ca 100644 --- a/test/fprime/common/models/serialize/test_types.py +++ b/test/fprime/common/models/serialize/test_types.py @@ -4,6 +4,7 @@ Created on Jun 25, 2020 @author: hpaulson, mstarch """ + import json from collections.abc import Iterable diff --git a/test/fprime/fbuild/test_build.py b/test/fprime/fbuild/test_build.py index 429f0c03..e806613d 100644 --- a/test/fprime/fbuild/test_build.py +++ b/test/fprime/fbuild/test_build.py @@ -6,6 +6,7 @@ @author mstarch """ + import os import pathlib