mirror of https://github.com/ecmwf/eccodes.git
Add convert.py
This commit is contained in:
parent
7f2d5a1910
commit
1b91729a41
|
@ -70,3 +70,4 @@ data/bufr/*test
|
|||
|
||||
build/
|
||||
*.back
|
||||
converted/
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--templates", default="j2")
|
||||
parser.add_argument("--target", default="converted")
|
||||
parser.add_argument("path", nargs="+")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(args.templates),
|
||||
undefined=StrictUndefined,
|
||||
)
|
||||
|
||||
|
||||
def to_camel_class(s):
|
||||
return "".join(x.capitalize() for x in s.lower().split("_"))
|
||||
|
||||
|
||||
def to_camel_member(s):
|
||||
s = to_camel_class(s)
|
||||
return s[0].lower() + s[1:]
|
||||
|
||||
|
||||
class Member:
|
||||
def __init__(self, s) -> None:
|
||||
bits = s.split()
|
||||
self.type = " ".join(bits[:-1])
|
||||
self.name = bits[-1] + "_"
|
||||
|
||||
if self.name[0] == "*":
|
||||
self.name = self.name[1:]
|
||||
self.type += "*"
|
||||
|
||||
|
||||
class Method:
|
||||
def __init__(self, name, result, args) -> None:
|
||||
self.name = name
|
||||
|
||||
self.result = result
|
||||
self.lines = []
|
||||
|
||||
args = [a.strip() for a in args.split(",")]
|
||||
self.args = ", ".join(args[1:])
|
||||
self.args_list = []
|
||||
for arg in [re.sub(r"\s+", " ", a).strip() for a in args]:
|
||||
bits = arg.split()
|
||||
type = " ".join(bits[:-1])
|
||||
name = bits[-1]
|
||||
self.args_list.append((type, name))
|
||||
|
||||
def add_line(self, line):
|
||||
self.lines.append(line)
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return "\n".join(self.lines)
|
||||
|
||||
def tidy_lines(self, klass):
|
||||
this = [r"\bself\b"]
|
||||
if self.args_list:
|
||||
type, name = self.args_list[0]
|
||||
# Check if the first argument is a pointer to the class
|
||||
if type == klass.type_name + "*":
|
||||
if re.match(r"^\w+$", name):
|
||||
this.append(rf"\b{name}\b")
|
||||
self.lines = [klass.tidy_line(n, this) for n in self.lines[1:-1]]
|
||||
|
||||
def has_this(self):
|
||||
for line in self.lines:
|
||||
if "this->" in line:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Class:
|
||||
rename = {}
|
||||
|
||||
substitute_str_top_level = {}
|
||||
substitute_re_top_level = {
|
||||
r"^#define\s+(\w+)\s+(-?\d+)": r"const long \1 = \2;",
|
||||
r"^#define\s+(\w+)\s+(-?\d+\.\d+)([eE]-?\d+)?": r"const double \1 = \2;",
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
path,
|
||||
class_,
|
||||
inherited_procs,
|
||||
other_procs,
|
||||
top_level,
|
||||
includes,
|
||||
factory_name,
|
||||
SUPER=[],
|
||||
IMPLEMENTS=[],
|
||||
MEMBERS=[],
|
||||
):
|
||||
assert factory_name is not None
|
||||
|
||||
self.class_ = class_
|
||||
self.name, self.cname = self.tidy_class_name(path)
|
||||
self.inherited_procs = inherited_procs
|
||||
self.factory_name = factory_name
|
||||
|
||||
self.self = None
|
||||
self.a = None
|
||||
self.includes = includes
|
||||
if SUPER:
|
||||
self.super, _ = self.tidy_class_name(SUPER[0])
|
||||
else:
|
||||
self.super, _ = self.tidy_class_name(class_)
|
||||
|
||||
self.members = [Member(m) for m in MEMBERS if m != ""]
|
||||
|
||||
for p in inherited_procs.values():
|
||||
p.tidy_lines(self)
|
||||
|
||||
self.inherited_methods = [
|
||||
p
|
||||
for p in inherited_procs.values()
|
||||
if p.name
|
||||
not in (
|
||||
"init",
|
||||
"destroy",
|
||||
)
|
||||
]
|
||||
init = [p for p in inherited_procs.values() if p.name == "init"]
|
||||
self.constructor = init[0] if init else Method("init", "void", "void")
|
||||
|
||||
init = [p for p in inherited_procs.values() if p.name == "destroy"]
|
||||
self.destructor = init[0] if init else Method("init", "void", "void")
|
||||
|
||||
for p in other_procs.values():
|
||||
p.tidy_lines(self)
|
||||
|
||||
self.private_methods = [p for p in other_procs.values() if p.has_this()]
|
||||
self.static_procs = [p for p in other_procs.values() if not p.has_this()]
|
||||
|
||||
self.top_level = defaultdict(list)
|
||||
for k, v in top_level.items():
|
||||
self.top_level[k] = [self.tidy_top_level(p) for p in v]
|
||||
|
||||
def dump(self):
|
||||
self.dump_header()
|
||||
self.dump_body()
|
||||
|
||||
def tidy_class_name(self, path):
|
||||
path, ext = os.path.splitext(path)
|
||||
cname = os.path.basename(path)
|
||||
|
||||
name = cname.replace(self.prefix, "")
|
||||
|
||||
name = to_camel_class(name)
|
||||
return self.rename.get(name, name), cname
|
||||
|
||||
def save(self, ext, content):
|
||||
target = os.path.join(args.target, *self.namespaces, f"{self.name}.{ext}")
|
||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
||||
with open(target, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
ret = os.system(f"clang-format -i {target}")
|
||||
assert ret == 0
|
||||
print(" ", target)
|
||||
|
||||
def dump_header(self):
|
||||
template = env.get_template(f"{self.class_}.h.j2")
|
||||
self.save(
|
||||
"h",
|
||||
template.render(
|
||||
name=self.name,
|
||||
super=self.super,
|
||||
members=self.members,
|
||||
includes=self.includes,
|
||||
inherited_methods=self.inherited_methods,
|
||||
private_methods=self.private_methods,
|
||||
static_procs=self.static_procs,
|
||||
constructor=self.constructor,
|
||||
destructor=self.destructor,
|
||||
namespaces=self.namespaces,
|
||||
namespace_reversed=reversed(self.namespaces),
|
||||
),
|
||||
)
|
||||
|
||||
def dump_body(self):
|
||||
template = env.get_template(f"{self.class_}.cc.j2")
|
||||
|
||||
def tidy_more(text):
|
||||
# Some more tidying
|
||||
|
||||
text = re.sub(
|
||||
r"/\*\s+This is used by make_class.pl\s+\*/",
|
||||
"",
|
||||
text,
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
return text
|
||||
|
||||
self.save(
|
||||
"cc",
|
||||
tidy_more(
|
||||
template.render(
|
||||
name=self.name,
|
||||
super=self.super,
|
||||
members=self.members,
|
||||
includes=self.includes,
|
||||
inherited_methods=self.inherited_methods,
|
||||
private_methods=self.private_methods,
|
||||
static_procs=self.static_procs,
|
||||
constructor=self.constructor,
|
||||
destructor=self.destructor,
|
||||
namespaces=self.namespaces,
|
||||
namespace_reversed=reversed(self.namespaces),
|
||||
include_header="/".join(self.namespaces + [f"{self.name}.h"]),
|
||||
top_level=self.top_level,
|
||||
factory_name=self.factory_name,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def tidy_line(self, line, this):
|
||||
line = re.sub(r"\bsuper->\b", f"{self.super}::", line)
|
||||
|
||||
for n in this:
|
||||
line = re.sub(n, "this", line)
|
||||
|
||||
if re.match(r".*\bthis\s+=", line):
|
||||
line = ""
|
||||
|
||||
if re.match(r".*\bsuper\s+=\s+\*\(this->cclass->super\)", line):
|
||||
line = ""
|
||||
|
||||
for k, v in self.substitute_str.items():
|
||||
line = line.replace(k, v)
|
||||
|
||||
for k, v in self.substitute_re.items():
|
||||
line = re.sub(k, v, line)
|
||||
|
||||
for m in self.members:
|
||||
name = m.name[:-1]
|
||||
line = re.sub(rf"\bthis->{name}\b", rf"this->{name}_", line)
|
||||
|
||||
m = re.match(r"\s*\breturn\s+(GRIB_\w*)\s*;", line)
|
||||
if m and m.group(1) != "GRIB_SUCCESS":
|
||||
# e = to_camel_class(m.group(1))
|
||||
# line = f"throw {e}Exception();"
|
||||
line = f"throw EccodesException({m.group(1)});"
|
||||
|
||||
return line
|
||||
|
||||
def tidy_top_level(self, line):
|
||||
for k, v in self.substitute_str_top_level.items():
|
||||
line = line.replace(k, v)
|
||||
|
||||
for k, v in self.substitute_re_top_level.items():
|
||||
line = re.sub(k, v, line)
|
||||
|
||||
return line
|
||||
|
||||
|
||||
class Accessor(Class):
|
||||
type_name = "grib_accessor"
|
||||
|
||||
namespaces = ["eccodes", "accessor"]
|
||||
prefix = "grib_accessor_class_"
|
||||
rename = {"Gen": "Generic"}
|
||||
|
||||
substitute_str = {
|
||||
"grib_handle_of_accessor(this)": "this->handle()",
|
||||
}
|
||||
substitute_re = {
|
||||
r"\bthis->length\b": "this->length_",
|
||||
r"\bthis->offset\b": "this->offset_",
|
||||
r"\bthis->flags\b": "this->flags_",
|
||||
r"\bthis->context\b": "this->context_",
|
||||
r"\bgrib_byte_offset\((\w+)\s*,": r"\1->pack_string(",
|
||||
r"\bgrib_byte_offset\((\w+)\s*\)": r"\1->byte_offset()",
|
||||
r"\bgrib_byte_count\((\w+)\s*\)": r"\1->byte_count()",
|
||||
r"\bgrib_pack_string\((\w+)\s*,": r"\1->pack_string(",
|
||||
r'\bDebugAssert\b': 'ASSERT',
|
||||
r'\bAssert\b': 'ASSERT',
|
||||
r'\bunpack_long\(this,': 'this->unpack_long(',
|
||||
}
|
||||
|
||||
def class_to_type(self):
|
||||
return self.cname.replace("_class_", "_")
|
||||
|
||||
|
||||
class Iterator(Class):
|
||||
pass
|
||||
|
||||
|
||||
class Action(Class):
|
||||
pass
|
||||
|
||||
|
||||
class Box(Class):
|
||||
pass
|
||||
|
||||
|
||||
class Dumper(Class):
|
||||
pass
|
||||
|
||||
|
||||
class Expression(Class):
|
||||
pass
|
||||
|
||||
|
||||
class Nearest(Class):
|
||||
pass
|
||||
|
||||
|
||||
CLASSES = dict(
|
||||
accessor=Accessor,
|
||||
iterator=Iterator,
|
||||
expression=Expression,
|
||||
nearest=Nearest,
|
||||
action=Action,
|
||||
box=Box,
|
||||
dumper=Dumper,
|
||||
)
|
||||
|
||||
|
||||
substitutions_re = {r"#define (\w+) (-?\d+)": r"const int \1 = \2;"}
|
||||
|
||||
|
||||
def make_class(path):
|
||||
in_def = False
|
||||
in_imp = False
|
||||
in_proc = False
|
||||
includes = []
|
||||
factory_name = None
|
||||
|
||||
definitions = {}
|
||||
inherited_procs = {}
|
||||
other_procs = {}
|
||||
top_level_lines = []
|
||||
top_level = defaultdict(list)
|
||||
print(path, file=sys.stderr)
|
||||
|
||||
f = open(path, "r")
|
||||
for line in f:
|
||||
stripped_line = line.strip()
|
||||
line = line.rstrip()
|
||||
|
||||
if stripped_line.startswith("START_CLASS_DEF"):
|
||||
in_def = True
|
||||
continue
|
||||
|
||||
if stripped_line.startswith("END_CLASS_DEF"):
|
||||
in_def = False
|
||||
continue
|
||||
|
||||
if stripped_line.startswith("/* START_CLASS_IMP */"):
|
||||
in_imp = True
|
||||
continue
|
||||
|
||||
if stripped_line.startswith("/* END_CLASS_IMP */"):
|
||||
in_imp = False
|
||||
continue
|
||||
|
||||
if in_imp:
|
||||
m = re.match(r"\s*\"(\w+)\",\s+/\* name \*/", stripped_line)
|
||||
if m:
|
||||
factory_name = m.group(1)
|
||||
continue
|
||||
|
||||
if in_def:
|
||||
if stripped_line.strip() == "":
|
||||
continue
|
||||
bits = [s.strip() for s in re.split(r"[=;]+", stripped_line)]
|
||||
try:
|
||||
what = bits[0]
|
||||
definitions.setdefault(what, [])
|
||||
definitions[what] += bits[1:]
|
||||
except KeyError:
|
||||
print(f"Unknown definition: {bits}")
|
||||
raise
|
||||
continue
|
||||
|
||||
m = re.match(r"static\s+([^(]+)\s+(\w+)\s*\(([^(]+)\)", line)
|
||||
if m:
|
||||
p = m.group(2)
|
||||
top_level[p] = [x for x in top_level_lines]
|
||||
top_level_lines = []
|
||||
|
||||
if p in definitions.get("IMPLEMENTS",[]):
|
||||
in_proc = True
|
||||
proc = inherited_procs[p] = Method(p, m.group(1), m.group(3))
|
||||
depth = stripped_line.count("{") - stripped_line.count("}")
|
||||
assert depth >= 0, line
|
||||
continue
|
||||
else:
|
||||
in_proc = True
|
||||
proc = Method(p, m.group(1), m.group(3))
|
||||
other_procs[p] = proc
|
||||
depth = stripped_line.count("{") - stripped_line.count("}")
|
||||
assert depth >= 0, line
|
||||
continue
|
||||
|
||||
if in_proc:
|
||||
proc.add_line(stripped_line)
|
||||
depth += stripped_line.count("{")
|
||||
depth -= stripped_line.count("}")
|
||||
assert depth >= 0, line
|
||||
if depth == 0:
|
||||
in_proc = False
|
||||
del proc
|
||||
continue
|
||||
|
||||
if stripped_line.startswith("#include"):
|
||||
if len(includes) == 0:
|
||||
# Forget lines before the first include
|
||||
top_level_lines = []
|
||||
includes.append(line[9:])
|
||||
continue
|
||||
|
||||
top_level_lines.append(line)
|
||||
|
||||
if definitions:
|
||||
top_level["-end-"] = top_level_lines
|
||||
class_ = definitions.pop("CLASS")[0]
|
||||
klass = CLASSES[class_](
|
||||
path=path,
|
||||
class_=class_,
|
||||
inherited_procs=inherited_procs,
|
||||
other_procs=other_procs,
|
||||
top_level=top_level,
|
||||
includes=includes,
|
||||
factory_name=factory_name,
|
||||
**definitions,
|
||||
)
|
||||
klass.dump()
|
||||
|
||||
|
||||
def main():
|
||||
for a in args.path:
|
||||
make_class(a)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* (C) Copyright 2005- ECMWF.
|
||||
*
|
||||
* This software is licensed under the terms of the Apache Licence Version 2.0
|
||||
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
||||
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
||||
*/
|
||||
|
||||
#include "{{ include_header }}"
|
||||
|
||||
{% for i in includes %}
|
||||
#include {{ i }}
|
||||
{% endfor %}
|
||||
|
||||
{% for n in namespaces %}namespace {{ n }} {
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for line in top_level['-start-'] %}{{ line }}
|
||||
{% endfor %}
|
||||
|
||||
{% for m in static_procs %}
|
||||
{% for line in top_level[m.name] %}{{ line }}
|
||||
{% endfor %}
|
||||
static {{ m.result }} {{ m.name }}({{ m.args }}) const {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for line in top_level['init'] %}{{ line }}
|
||||
{% endfor %}
|
||||
{{ name }}({{constructor.args}}) {
|
||||
{{ constructor.body }}
|
||||
}
|
||||
|
||||
{% for line in top_level['destroy'] %}{{ line }}
|
||||
{% endfor %}
|
||||
~{{ name }}() {
|
||||
{{ destructor.body }}
|
||||
}
|
||||
|
||||
{% for m in inherited_methods %}
|
||||
{% for line in top_level[m.name] %}{{ line }}
|
||||
{% endfor %}
|
||||
|
||||
{{ m.result }} {{ name }}::{{ m.name }}({{ m.args }}) const {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for m in private_methods %}
|
||||
{% for line in top_level[m.name] %}{{ line }}
|
||||
{% endfor %}
|
||||
{{ m.result }} {{ name }}::{{ m.name }}({{ m.args }}) const {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
void {{ name }}::print(std::ostream& s) const {
|
||||
s << "{{ name }}["
|
||||
{% for m in members %} << "{{ m.name }}=" << {{ m.name }} {% if not loop.last %}<< ',' {% endif %}
|
||||
{% endfor %} << "]";
|
||||
}
|
||||
|
||||
{% for line in top_level['-end-'] %}{{ line }}
|
||||
{% endfor %}
|
||||
|
||||
static AccessorMaker<{{ name }}> maker("{{ factory_name }}");
|
||||
|
||||
{% for n in namespace_reversed %}} // namespace {{ n }}
|
||||
{% endfor %}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* (C) Copyright 2005- ECMWF.
|
||||
*
|
||||
* This software is licensed under the terms of the Apache Licence Version 2.0
|
||||
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
||||
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
||||
*/
|
||||
|
||||
{% for i in includes %}
|
||||
#include {{ i }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for n in namespaces %}namespace {{ n }} {
|
||||
{% endfor %}
|
||||
|
||||
class {{ name }} : public {{ super }} {
|
||||
|
||||
protected:
|
||||
|
||||
{{ name }}({{constructor.args}});
|
||||
virtual ~{{ name }}();
|
||||
|
||||
private:
|
||||
|
||||
// Inherited methods
|
||||
|
||||
{% for m in inherited_methods %}
|
||||
virtual {{ m.result }} {{ m.name }}({{ m.args }}) const override;{% endfor %}
|
||||
virtual void print(std::ostream& s) const override;
|
||||
|
||||
// Private methods
|
||||
{% for m in private_methods %}
|
||||
{{ m.result }} {{ m.name }}({{ m.args }}) const;{% endfor %}
|
||||
|
||||
|
||||
// Members
|
||||
{% for m in members %}{{ m.type }} {{ m.name }};
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
// friends
|
||||
friend std::ostream& operator<<(std::ostream& s, const {{ name }}& p) { p.print(s); return s; }
|
||||
};
|
||||
|
||||
{% for n in namespace_reversed %}} // namespace {{ n }}
|
||||
{% endfor %}
|
Loading…
Reference in New Issue