From e62205769fac930cde054f1bae209aaff6651ee4 Mon Sep 17 00:00:00 2001 From: Kai Huuhko Date: Sun, 22 Jun 2014 20:29:36 +0300 Subject: [PATCH] Eolian: Initial code for generating enums --- scripts/eolian_generate.py | 175 ++++++++++++++++++++++++++++--------- 1 file changed, 133 insertions(+), 42 deletions(-) diff --git a/scripts/eolian_generate.py b/scripts/eolian_generate.py index e408bab..6a92ca1 100755 --- a/scripts/eolian_generate.py +++ b/scripts/eolian_generate.py @@ -4,6 +4,8 @@ import os import textwrap import keyword +from collections import Counter + from argparse import ArgumentParser parser = ArgumentParser(description="Python generator for eolian") # parser.add_argument( @@ -32,6 +34,11 @@ eolian.init() DOCSTRINGS_ENABLED = True import re + +enum_re = re.compile(r"typedef\s+enum\s*{([^}]*)}([^;]+);") +memb_re = re.compile( + r"\n +(?P\w+)[(), =<\d\-+]*([/*<]* (?P[^/]+)? ?\*/)?") + docstring_replacements = ( ("@brief ", ""), (re.compile(r"@ingroup .+", re.S), r""), @@ -79,6 +86,9 @@ return_type_mapping = { "Eo *": ("_Eo_Base", 'object_from_instance({0})'), } +anon_enums = [] # (filename, enum_members), ... +enums = {} # enum_name: (filename, enum_members), ... + def conv_cls_name(name): s = name.split("_") @@ -147,6 +157,17 @@ class Generator(object): def printout(self): return "\n".join(self.result) + def write_enum(self, name): + filename, members = enums[name] + self.write("from efl.utils.enum import IntEnum") + self.write("# File: " + filename) + self.write("class %s(IntEnum):" % (name)) + self.indent() + for value, comment in members: + self.write("%s # %s" % (value, comment)) + self.dedent() + self.write() + class PyxGenerator(Generator): def method_header_write(self, name, params, clsm=False): @@ -195,6 +216,7 @@ class Method(object): self.cdefs = [] self.returns = [] self.docs = [] + self.enum_types = [] self.ret_type = None self.py_name = None self.eo_prefix = eo_prefix @@ -227,7 +249,11 @@ class Method(object): pdir, ptype, name, desc = p.information ptype2 = ptype.replace("const ", "").replace("unsigned ", "") if not ptype2 in param_type_mapping: - raise TypeError("Unknown param type: %s" % (ptype2)) + if not ptype2 in enums: + raise TypeError("Unknown param type: %s" % (ptype2)) + else: + self.enum_types.append(ptype2) + enums_counter[ptype2] += 1 self.params.append((ptype, name)) if pdir == eolian.ParameterDir.IN: self.py_params.append((ptype, name)) @@ -247,7 +273,11 @@ class Method(object): if ret_type: ret_type2 = ret_type.replace("const ", "").replace("unsigned ", "") if not ret_type2 in return_type_mapping: - raise TypeError("Unknown return type: %s" % (ret_type2)) + if not ret_type2 in enums: + raise TypeError("Unknown return type: %s" % (ret_type2)) + else: + self.enum_types.append(ret_type2) + enums_counter[ret_type2] += 1 self.ret_type = ret_type ret_desc = func.return_comment_get(func.type) @@ -352,6 +382,7 @@ class Property(object): super(type(self), self).__init__() self.docs = [] self.eo_prefix = eo_prefix + self.enum_types = [] @classmethod def parse(cls, func, eo_prefix): @@ -394,7 +425,11 @@ class Property(object): assert pdir == eolian.ParameterDir.IN, "prop has other than IN" ptype2 = ptype.replace("const ", "").replace("unsigned ", "") if not ptype2 in param_type_mapping: - raise TypeError("Unknown param type: %s" % (ptype2)) + if not ptype2 in enums: + raise TypeError("Unknown param type: %s" % (ptype2)) + else: + self.enum_types.append(ptype2) + enums_counter[ptype2] += 1 m.params.append((ptype, name)) py_params.append(name) m.c_params.append((ptype, name)) @@ -412,7 +447,12 @@ class Property(object): ret_type2 = m.ret_type.replace( "const ", "").replace("unsigned ", "") if not ret_type2 in return_type_mapping: - raise TypeError("Unknown return type: %s" % (ret_type2)) + if not ret_type2 in enums: + raise TypeError( + "Unknown return type: %s" % (ret_type2)) + else: + self.enum_types.append(ret_type2) + enums_counter[ret_type2] += 1 if func.type == eolian.FunctionType.PROP_GET or \ func.type == eolian.FunctionType.PROPERTY: @@ -425,7 +465,11 @@ class Property(object): assert pdir == eolian.ParameterDir.IN, "prop has other than IN" ptype2 = ptype.replace("const ", "").replace("unsigned ", "") if not ptype2 in param_type_mapping: - raise TypeError("Unknown param type: %s" % (ptype2)) + if not ptype2 in enums: + raise TypeError("Unknown param type: %s" % (ptype2)) + else: + self.enum_types.append(ptype2) + enums_counter[ptype2] += 1 m.params.append((ptype, name)) m.cdefs.append((ptype, name)) m.c_params.append((ptype, "&%s" % (name))) @@ -436,11 +480,20 @@ class Property(object): ret_type2 = m.ret_type.replace( "const ", "").replace("unsigned ", "") if not ret_type2 in return_type_mapping: - raise TypeError("Unknown return type: %s" % (ret_type2)) + if not ret_type2 in enums: + raise TypeError( + "Unknown return type: %s" % (ret_type2)) + else: + self.enum_types.append(ret_type2) + enums_counter[ret_type2] += 1 return self def pyx_generate(self, gen): + if self.enum_types: + for t in set(self.enum_types): + gen.write_enum(t) + gen.write("property %s:" % (self.name)) gen.indent() @@ -583,8 +636,9 @@ class Class(object): for ctor in ctors: try: o = Constructor.parse(ctor, prefix) - except Exception: - log.exception( + except Exception as e: + log.warn(e) + log.info( "Skipping %s.%s because of an exception" % (cls.name, ctor.name)) continue @@ -595,8 +649,9 @@ class Class(object): for prop in props: try: o = Property.parse(prop, prefix) - except Exception: - log.exception( + except Exception as e: + log.warn(e) + log.info( "Skipping %s.%s because of an exception" % (cls.name, prop.name)) continue @@ -606,8 +661,9 @@ class Class(object): for method in methods: try: o = Method.parse(method, prefix) - except Exception: - log.exception( + except Exception as e: + log.warn(e) + log.info( "Skipping %s.%s because of an exception" % (cls.name, method.name)) continue @@ -705,42 +761,77 @@ py_path = os.path.join(args.output, "__init__" + ".py") if os.path.exists(py_path): os.remove(py_path) + for path in args.paths: for dirpath, dirnames, filenames in os.walk(path): - eolian.directory_scan(dirpath) - eolian.all_eo_files_parse() for filename in filenames: - if filename.endswith(".eo"): - f = os.path.join(dirpath, filename) - if not eolian.eo_file_parse(f): - log.warn("Errors in parsing %s" % (f)) - cls = eolian.class_find_by_file(f) - try: - gencls = Class.parse(cls) - except Exception: - log.exception( - "Skipping %s because of an exception" % (cls.name)) - continue + if filename.endswith(".h"): + with open(os.path.join(dirpath, filename), "r") as h: + s = h.read() + matches = re.finditer(enum_re, s) + for m in matches: + name = m.group(2) + members = m.group(1) + log.debug("==== %s ====" % (name)) + log.debug("--- RAW ---") + log.debug(members) + members = re.finditer(memb_re, members) + members2 = [] + log.debug("--- REGEXED ---") + for match in members: + value = match.group("value") + comment = match.group("comment") + if comment: + comment2 = [] + for line in comment.splitlines(): + comment2.append(line.strip()) + comment = " ".join(comment2) + log.debug(value, comment) + members2.append((value, comment)) + if name: + name = name.strip("_ ") + enums[name] = (filename, members2) + else: + anon_enums.append((filename, members2)) - pyxgen = PyxGenerator() - gencls.pyx_generate(pyxgen) +enums_counter = Counter(enums.keys()) - f_base = os.path.splitext(filename)[0] - pxi_path = os.path.join(args.output, f_base + ".pxi") - o = pyxgen.printout() - if o: - with open(pxi_path, "w") as f: - f.write(o.encode("utf-8")) - log.info(pxi_path + " written") +with open(py_path, "a") as py_f: + for path in args.paths: + for dirpath, dirnames, filenames in os.walk(path): + eolian.directory_scan(dirpath) + eolian.all_eo_files_parse() + for filename in filenames: + if filename.endswith(".eo"): + f = os.path.join(dirpath, filename) + if not eolian.eo_file_parse(f): + log.warn("Errors in parsing %s" % (f)) + cls = eolian.class_find_by_file(f) + try: + gencls = Class.parse(cls) + except Exception: + log.exception( + "Skipping %s because of an exception" % (cls.name)) + continue - pygen = Generator() - gencls.py_generate(pygen) + pyxgen = PyxGenerator() + gencls.pyx_generate(pyxgen) - o = pygen.printout() - if o: - with open(py_path, "a") as f: - f.write(o.encode("utf-8")) - f.write("\n") - log.info(py_path + " appended") + f_base = os.path.splitext(filename)[0] + pxi_path = os.path.join(args.output, f_base + ".pxi") + o = pyxgen.printout() + if o: + with open(pxi_path, "w") as f: + f.write(o.encode("utf-8")) + log.info(pxi_path + " written") + + pygen = Generator() + gencls.py_generate(pygen) + + o = pygen.printout() + if o: + py_f.write(o.encode("utf-8")) + py_f.write("\n") + log.info(py_path + " appended") eolian.shutdown()