From 41199d26b94b827f7dca510a84f7a3d3fbb880d2 Mon Sep 17 00:00:00 2001 From: cyrillemidingoyi Date: Sat, 14 Sep 2024 17:36:04 +0200 Subject: [PATCH] improve transpiler python to Crop2ML --- requirements.txt | 2 +- src/pycropml/transpiler/antlr_py/createXml.py | 1 - .../antlr_py/extract_metadata_from_comment.py | 1 - .../antlr_py/python/api_transform.py | 1 - .../antlr_py/python/pythonExtraction.py | 105 +++++++++++++----- .../antlr_py/python/pythonTransformer.py | 5 +- .../transpiler/antlr_py/python/run.py | 4 + src/pycropml/transpiler/ast_transform.py | 1 - 8 files changed, 85 insertions(+), 35 deletions(-) diff --git a/requirements.txt b/requirements.txt index 62718a0e..618f7e8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,7 +26,7 @@ path six nbformat numpy==1.16.6 -Cython +Cython==0.29.21 nbsphinx networkx graphviz diff --git a/src/pycropml/transpiler/antlr_py/createXml.py b/src/pycropml/transpiler/antlr_py/createXml.py index 28a31436..fc9e0c34 100644 --- a/src/pycropml/transpiler/antlr_py/createXml.py +++ b/src/pycropml/transpiler/antlr_py/createXml.py @@ -43,7 +43,6 @@ def run_unit(self): for inp in md.inputs: if "variablecategory" in dir(inp): if inp.datatype.endswith("ARRAY"): - print(inp) inputs.append(ns.Input(name=inp.name, description=inp.description, inputtype=inp.inputtype, variablecategory=inp.variablecategory, datatype=inp.datatype, len=inp.len, max=inp.max, min = inp.min, default=inp.default, unit=inp.unit)) else: inputs.append(ns.Input(name=inp.name, description=inp.description, inputtype=inp.inputtype, variablecategory=inp.variablecategory, datatype=inp.datatype, max=inp.max, min = inp.min, default=inp.default, unit=inp.unit)) else: diff --git a/src/pycropml/transpiler/antlr_py/extract_metadata_from_comment.py b/src/pycropml/transpiler/antlr_py/extract_metadata_from_comment.py index 82563051..21031a0f 100644 --- a/src/pycropml/transpiler/antlr_py/extract_metadata_from_comment.py +++ b/src/pycropml/transpiler/antlr_py/extract_metadata_from_comment.py @@ -137,7 +137,6 @@ def extract_compo(comment): head[keywords[i]] = re.search(p, comment).group(keywords[i]) i = i + 1 m = ModelComposition(head) - # description element of modelUnit (Title, Authors, Reference, Institution, Abstract) pat_description = r'-\s*Description:\s*(.*?)(?=\n\s*[#!/]*\s*-\s*inputs|\n\s*[#!/]*\s*-\s*outputs|$)' text_description = re.search(pat_description, comment, re.DOTALL).group(1) diff --git a/src/pycropml/transpiler/antlr_py/python/api_transform.py b/src/pycropml/transpiler/antlr_py/python/api_transform.py index abdfe8d3..bb9339ff 100644 --- a/src/pycropml/transpiler/antlr_py/python/api_transform.py +++ b/src/pycropml/transpiler/antlr_py/python/api_transform.py @@ -89,7 +89,6 @@ def float_expander(type, message, args): def len_expander(type, message, args): receiver_type = args[0]['pseudo_type'] if isinstance(receiver_type, list): - print(receiver_type, "ioooo") a = receiver_type[0] else: a = receiver_type diff --git a/src/pycropml/transpiler/antlr_py/python/pythonExtraction.py b/src/pycropml/transpiler/antlr_py/python/pythonExtraction.py index e8269952..e335ecca 100644 --- a/src/pycropml/transpiler/antlr_py/python/pythonExtraction.py +++ b/src/pycropml/transpiler/antlr_py/python/pythonExtraction.py @@ -7,7 +7,18 @@ from openalea.core.pkgmanager import PackageManager from pycropml.transpiler.antlr_py.codeExtraction import extraction import re +import copy +def listdictvalues(dictlist:list) -> list: + """Extract the values of a list of dictionnaries + + Args: + dictlist (list): list of dictionnaries + + Returns: + list: list of values + """ + return [v for d in dictlist for v in d.values()] class PythonExtraction(MetaExtraction): def __init__(self): @@ -26,51 +37,91 @@ def modelunit(self, file, tree): def getMethod(self, tree): self.getTypeNode(tree, "function_definition") return self.getTree + + def orderedvar(self, mdata, tree): + mu_inputs = [m.name for m in tree.params] + mu_outputs = [m.name for m in tree.block[-1].value.elements] + inps = [] + outs = [] + for n in mu_inputs: + for inp in mdata.inputs: + if inp.name == n: + inps.append(inp) + for n in mu_outputs: + for out in mdata.outputs: + if out.name == n: + outs.append(out) + mdata.inputs = inps + mdata.outputs = outs + return mdata + def modelcomposition(self, file, models, tree): self.mc = extract_compo(file) + self.getTypeNode(tree, "function_definition") + mc_def_tree = self.getTree + mc_inputs = [m.name for m in mc_def_tree[0].params] + mc_outputs = [m.name for m in mc_def_tree[0].block[-1].value.elements] + self.getTypeNode(mc_def_tree[0].block, "assignment") + list_assign = self.getTree inputlink = [] outputlink = [] + internallink = [] inp = {} - #name = re.findall(r'(def\s+.+\()', py_unit)[0].replace("def", "").replace("(", "").strip() funcs = self.getMethod(tree) - print(funcs) algo = [f for f in funcs if f.name.startswith("model")] self.getTypeNode(algo[0].block,"custom_call") call = self.getTree self.mc.model = [c.function.split("model_")[-1] for c in call] - print(self.mc.model) - print([n.name for n in models]) inps, outs = [], [] md = [n for m in self.mc.model for n in models if m.lower() == n.name.split("model_")[-1].lower()] self.mc.model = [n.name for n in md] - inps = [n.name for m in md for n in m.inputs ] - outs = [n.name for m in md for n in m.outputs ] - m_in = set(inps) - set(outs) - z = {} - internallink= [] - for m in md: - vi = list(set([n.name for n in m.inputs ]).intersection(m_in)) - vo = [n.name for n in m.outputs] - for v in vi: - inputlink.append({"target": m.name + "." + v, "source":v}) - for v in vo: z.update({v:m.name}) - - for k, v in z.items(): - outputlink.append({"source": v + "." + k, "target":k}) - - for i in range(0, len(md)-1): + inps = {m.name:[n.name for n in m.inputs] for m in md} + outs = {m.name:[n.name for n in m.outputs] for m in md} + var_int = [] + var_out = [] # variables that are outputs of model units + len_r = len(md) - 1 if len(md) > 1 else len(md) + res_in = {} + res_out = {} + for i in range(0, len_r): mi = md[i] - for j in range(i+1, len(md)): - mj = md[j] - vi = list(set([n.name for n in mi.outputs ]).intersection(set([n.name for n in mj.inputs ]))) - if vi: - for k in vi: - internallink.append({"source": mi.name + "." + k, "target":mj.name + "." + k}) + mi_inp = inps[mi.name] + mi_out = outs[mi.name] + mi_inp_p = set(mi_inp).intersection(set(mc_inputs)) # inputs of mi that are also inputs of the model composition + mi_out_p = set(mi_out).intersection(set(mc_outputs)) # outputs of mi that are also outputs ... + mi_inp_f = mi_inp_p - set(var_out) # inputs of mi that are not outputs of the previous model units + if len(md) > 1: + for j in range(i+1, len_r+1): + mj = md[j] + mj_inp = inps[mj.name] + zi = list(mi_out.intersection(set(mj_inp))) + var_int.extend(list(zi)) + for k in zi: + internallink.append({"source": mi.name + "." + k, "target":mj.name + "." + k}) + var_out.extend(mi_out) + mi_out_f = mi_out_p - set(var_int) # outputs of mi that are not used as intermediate variables are considered as outputs of the model composition + res_in.update({mi.name:mi_inp_f}) + res_out.update({mi.name:mi_out_f}) + + for k in mc_inputs: + for m in md: + if k in res_in[m.name]: + inputlink.append({"target": m.name + "." + k, "source":k}) + for k in mc_outputs: + for m in md: + if k in res_out[m.name]: + outputlink.append({"source": m.name + "." + k, "target":k}) + + ilink = {} + for a in list_assign: + if "name" in dir(a.value) and a.value.name in mc_inputs: + for m in md: + if a.target.name in inps[m.name]: + if a.value.name not in ilink: ilink[a.value.name] = [] + ilink[a.value.name].append({m.name: a.target.name}) self.mc.inputlink = inputlink self.mc.outputlink = outputlink self.mc.internallink = internallink - print(self.mc.model) return self.mc \ No newline at end of file diff --git a/src/pycropml/transpiler/antlr_py/python/pythonTransformer.py b/src/pycropml/transpiler/antlr_py/python/pythonTransformer.py index 9c605ac8..dc186e1f 100644 --- a/src/pycropml/transpiler/antlr_py/python/pythonTransformer.py +++ b/src/pycropml/transpiler/antlr_py/python/pythonTransformer.py @@ -251,9 +251,9 @@ def transformer(self): body = self.visit(self.tree) self.type_env.top['__name__'] = "str" self.q=None - + #'definition':self.signature, #print(self.type_env.values) - return {'type': 'module','definition':self.signature, 'iterators':self.iterators, 'body': body if isinstance(body, list) else [body]} + return {'type': 'module', 'iterators':self.iterators, 'body': body if isinstance(body, list) else [body]} def visit_definitions(self): definitions = [] @@ -655,7 +655,6 @@ def visit_expr(self, node, AWAIT,expr, atom, trailer,POWER,ADD,MINUS,NOT_OP,STA self.accessReturn(x[0]) q = self.q["pseudo_type"] else: - x = [f for f in self.signature if f.name.NAME == message] argx = [a["pseudo_type"] for a in self.visit(x[0].typedargslist)] #self._definition_index["functions"][message] = self.visit(x[0]) diff --git a/src/pycropml/transpiler/antlr_py/python/run.py b/src/pycropml/transpiler/antlr_py/python/run.py index f5b990e1..c7f974ee 100644 --- a/src/pycropml/transpiler/antlr_py/python/run.py +++ b/src/pycropml/transpiler/antlr_py/python/run.py @@ -38,6 +38,8 @@ def translate(inout, meth): inittag_begin = "#"+init_tags[0] inittag_end = "#"+init_tags[1] + + def run_python(components, package): create_repo(package) @@ -171,10 +173,12 @@ def run_python(components, package): fi.write(extcode + '\n') models.append(mdata) + mdata = z.orderedvar(mdata,meth) generate_unitfile(package, mdata, package_name) break else: + mdata = z.orderedvar(mdata,meth) models.append(mdata) generate_unitfile(package, mdata, package_name) diff --git a/src/pycropml/transpiler/ast_transform.py b/src/pycropml/transpiler/ast_transform.py index fa9c45d5..f3b399fd 100644 --- a/src/pycropml/transpiler/ast_transform.py +++ b/src/pycropml/transpiler/ast_transform.py @@ -233,7 +233,6 @@ def visit_singleassignmentnode(self, node, lhs, rhs, location): e, value_node['pseudo_type'], "can't change the type of variable %s in %s " % (name, self.function_name)) else: #if value_node["type"] =="custom_call" and value_node["pseudo_type"] is None: value_node["pseudo_type"] = e - print(value_node) if value_node["type"] != "none": a = self._compatible_types(e, value_node['pseudo_type'], "can't change the type of variable %s in %s at %s " % ( name, self.function_name, location[0]))