Swift: move toposort in schema.py

This makes the result of code generation independent of the order
in which classes are defined in the schema, and makes additional
topological sorting not required.

Being independent from schema order will be important for reviewing the
move to a pure python schema, as generated code will be left untouched.
This commit is contained in:
Paolo Tranquilli
2022-09-21 10:44:39 +02:00
parent b49487cf42
commit a50f3f752b
8 changed files with 6413 additions and 6425 deletions

View File

@@ -16,7 +16,6 @@ import pathlib
from typing import Dict
import inflection
from toposort import toposort_flatten
from swift.codegen.lib import cpp, schema
@@ -71,13 +70,9 @@ class Processor:
)
def get_classes(self):
grouped = {pathlib.Path(): {}}
ret = {pathlib.Path(): []}
for k, cls in self._classmap.items():
grouped.setdefault(cls.dir, {}).update({k: cls})
ret = {}
for dir, map in grouped.items():
inheritance_graph = {k: {b for b in cls.bases if b in map} for k, cls in map.items()}
ret[dir] = [self._get_class(cls) for cls in toposort_flatten(inheritance_graph)]
ret.setdefault(cls.dir, []).append(self._get_class(cls.name))
return ret

View File

@@ -28,7 +28,6 @@ import typing
import itertools
import inflection
from toposort import toposort_flatten
from swift.codegen.lib import schema, ql
@@ -263,9 +262,7 @@ def generate(opts, renderer):
imports = {}
inheritance_graph = {name: cls.bases for name, cls in data.classes.items()}
toposorted_names = toposort_flatten(inheritance_graph)
db_classes = [classes[name] for name in toposorted_names if not classes[name].ipa]
db_classes = [cls for cls in classes.values() if not cls.ipa]
renderer.render(ql.DbClasses(db_classes), out / "Raw.qll")
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
@@ -286,8 +283,7 @@ def generate(opts, renderer):
include_file = stub_out.with_suffix(".qll")
renderer.render(ql.ImportList(list(imports.values())), include_file)
toposorted_classes = [classes[name] for name in toposorted_names]
renderer.render(ql.GetParentImplementation(toposorted_classes), out / 'ParentChild.qll')
renderer.render(ql.GetParentImplementation(list(classes.values())), out / 'ParentChild.qll')
for c in data.classes.values():
if _should_skip_qltest(c, data.classes):

View File

@@ -4,6 +4,7 @@ import pathlib
import re
from dataclasses import dataclass, field
from typing import List, Set, Union, Dict, ClassVar, Optional
from toposort import toposort_flatten
import yaml
@@ -167,4 +168,17 @@ def load(path):
cls.bases = [root_class_name]
classes[root_class_name].derived.add(name)
return Schema(classes=classes, includes=set(data.get("_includes", [])))
groups = {}
for name, cls in classes.items():
groups.setdefault(cls.dir, []).append(name)
sorted_classes = {}
for dir in sorted(groups):
group = groups[dir]
inheritance = {name: classes[name].bases for name in group}
for name in toposort_flatten(inheritance):
sorted_classes[name] = classes[name]
return Schema(classes=sorted_classes, includes=set(data.get("_includes", [])))

View File

@@ -56,23 +56,6 @@ def test_two_class_hierarchy(generate):
]
def test_complex_hierarchy_topologically_ordered(generate):
a = cpp.Class(name="A")
b = cpp.Class(name="B")
c = cpp.Class(name="C", bases=[a])
d = cpp.Class(name="D", bases=[a])
e = cpp.Class(name="E", bases=[b, c, d], final=True, trap_name="Es")
f = cpp.Class(name="F", bases=[c], final=True, trap_name="Fs")
assert generate([
schema.Class(name="F", bases=["C"]),
schema.Class(name="B", derived={"E"}),
schema.Class(name="D", bases=["A"], derived={"E"}),
schema.Class(name="C", bases=["A"], derived={"E", "F"}),
schema.Class(name="E", bases=["B", "C", "D"]),
schema.Class(name="A", derived={"C", "D"}),
]) == [a, b, c, d, e, f]
@pytest.mark.parametrize("type,expected", [
("a", "a"),
("string", "std::string"),
@@ -154,8 +137,8 @@ def test_classes_with_dirs(generate_grouped):
assert generate_grouped([
schema.Class(name="A"),
schema.Class(name="B", dir=pathlib.Path("foo")),
schema.Class(name="C", bases=["CBase"], dir=pathlib.Path("bar")),
schema.Class(name="CBase", derived={"C"}, dir=pathlib.Path("bar")),
schema.Class(name="C", bases=["CBase"], dir=pathlib.Path("bar")),
schema.Class(name="D", dir=pathlib.Path("foo/bar/baz")),
]) == {
".": [cpp.Class(name="A", trap_name="As", final=True)],

View File

@@ -177,10 +177,10 @@ def test_hierarchy_imports(generate_import_list):
def test_hierarchy_children(generate_children_implementations):
assert generate_children_implementations([
schema.Class("D", bases=["B", "C"]),
schema.Class("C", bases=["A"], derived={"D"}),
schema.Class("B", bases=["A"], derived={"D"}),
schema.Class("A", derived={"B", "C"}),
schema.Class("B", bases=["A"], derived={"D"}),
schema.Class("C", bases=["A"], derived={"D"}),
schema.Class("D", bases=["B", "C"]),
]) == ql.GetParentImplementation(
classes=[ql.Class(name="A"),
ql.Class(name="B", bases=["A"], imports=[

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff