Skip to content

Commit f3cc6ae

Browse files
authored
Merge pull request #10516 from github/redsun82/swift-python-schema
Swift: express the schema in Python
2 parents 48f938e + 3983c36 commit f3cc6ae

22 files changed

+1779
-1756
lines changed

swift/.pep8

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pep8]
2+
ignore = E302
3+
max_line_length = 120

swift/BUILD.bazel

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ load("@rules_pkg//:install.bzl", "pkg_install")
33
load("//:defs.bzl", "codeql_platform")
44
load("//misc/bazel:pkg_runfiles.bzl", "pkg_runfiles")
55

6+
filegroup(
7+
name = "schema",
8+
srcs = ["schema.py"],
9+
visibility = ["//swift:__subpackages__"],
10+
)
11+
12+
filegroup(
13+
name = "schema_includes",
14+
srcs = glob(["*.dbscheme"]),
15+
visibility = ["//swift:__subpackages__"],
16+
)
17+
618
pkg_files(
719
name = "dbscheme_files",
820
srcs = [

swift/codegen/BUILD.bazel

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
load("@swift_codegen_deps//:requirements.bzl", "requirement")
22

3-
filegroup(
4-
name = "schema",
5-
srcs = ["schema.yml"],
6-
visibility = ["//swift:__subpackages__"],
7-
)
8-
9-
filegroup(
10-
name = "schema_includes",
11-
srcs = glob(["*.dbscheme"]),
12-
visibility = ["//swift:__subpackages__"],
13-
)
14-
153
py_binary(
164
name = "codegen",
175
srcs = ["codegen.py"],
186
data = [
19-
":schema",
20-
":schema_includes",
7+
"//swift:schema",
8+
"//swift:schema_includes",
219
"//swift/codegen/templates:cpp",
2210
"//swift/codegen/templates:trap",
2311
],
2412
visibility = ["//swift:__subpackages__"],
25-
deps = ["//swift/codegen/generators"],
13+
deps = [
14+
"//swift/codegen/generators",
15+
],
2616
)

swift/codegen/codegen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def _parse_args() -> argparse.Namespace:
2323
p.add_argument("--swift-dir", type=_abspath, default=paths.swift_dir,
2424
help="the directory that should be regarded as the root of the swift codebase. Used to compute QL "
2525
"imports and in some comments (default %(default)s)")
26-
p.add_argument("--schema", type=_abspath, default=paths.swift_dir / "codegen/schema.yml",
26+
p.add_argument("--schema", type=_abspath, default=paths.swift_dir / "schema.py",
2727
help="input schema file (default %(default)s)")
2828
p.add_argument("--dbscheme", type=_abspath, default=paths.swift_dir / "ql/lib/swift.dbscheme",
2929
help="output file for dbscheme generation, input file for trap generation (default %(default)s)")

swift/codegen/generators/cppgen.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,16 @@ def _get_class(self, name: str) -> cpp.Class:
7070
)
7171

7272
def get_classes(self):
73-
ret = {pathlib.Path(): []}
73+
ret = {'': []}
7474
for k, cls in self._classmap.items():
75-
ret.setdefault(cls.dir, []).append(self._get_class(cls.name))
75+
ret.setdefault(cls.group, []).append(self._get_class(cls.name))
7676
return ret
7777

7878

7979
def generate(opts, renderer):
8080
assert opts.cpp_output
81-
processor = Processor(schema.load(opts.schema).classes)
81+
processor = Processor(schema.load_file(opts.schema).classes)
8282
out = opts.cpp_output
8383
for dir, classes in processor.get_classes().items():
84-
include_parent = (dir != pathlib.Path())
85-
renderer.render(cpp.ClassList(classes, opts.schema, include_parent), out / dir / "TrapClasses")
84+
renderer.render(cpp.ClassList(classes, opts.schema,
85+
include_parent=bool(dir)), out / dir / "TrapClasses")

swift/codegen/generators/dbschemegen.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
The type hierarchy will be translated to corresponding `union` declarations.
1515
"""
1616

17-
import pathlib
18-
1917
import inflection
2018

2119
from swift.codegen.lib import schema
2220
from swift.codegen.lib.dbscheme import *
21+
from typing import Set, List
2322

2423
log = logging.getLogger(__name__)
2524

@@ -35,7 +34,7 @@ def cls_to_dbscheme(cls: schema.Class):
3534
""" Yield all dbscheme entities needed to model class `cls` """
3635
if cls.derived:
3736
yield Union(dbtype(cls.name), (dbtype(c) for c in cls.derived))
38-
dir = cls.dir if cls.dir != pathlib.Path() else None
37+
dir = pathlib.Path(cls.group) if cls.group else None
3938
# output a table specific to a class only if it is a leaf class or it has 1-to-1 properties
4039
# Leaf classes need a table to bind the `@` ids
4140
# 1-to-1 properties are added to a class specific table
@@ -104,7 +103,7 @@ def generate(opts, renderer):
104103
input = opts.schema
105104
out = opts.dbscheme
106105

107-
data = schema.load(input)
106+
data = schema.load_file(input)
108107

109108
dbscheme = Scheme(src=input.relative_to(opts.swift_dir),
110109
includes=get_includes(data, include_dir=input.parent, swift_dir=opts.swift_dir),

swift/codegen/generators/qlgen.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ class RootElementHasChildren(Error):
5151
pass
5252

5353

54+
class NoClasses(Error):
55+
pass
56+
57+
5458
def get_ql_property(cls: schema.Class, prop: schema.Property, prev_child: str = "") -> ql.Property:
5559
args = dict(
5660
type=prop.type if not prop.is_predicate else "predicate",
@@ -103,7 +107,7 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
103107
bases=cls.bases,
104108
final=not cls.derived,
105109
properties=properties,
106-
dir=cls.dir,
110+
dir=pathlib.Path(cls.group or ""),
107111
ipa=bool(cls.ipa),
108112
**pragmas,
109113
)
@@ -127,7 +131,7 @@ def get_ql_ipa_class_db(name: str) -> ql.Synth.FinalClassDb:
127131
def get_ql_ipa_class(cls: schema.Class):
128132
if cls.derived:
129133
return ql.Synth.NonFinalClass(name=cls.name, derived=sorted(cls.derived),
130-
root=(cls.name == schema.root_class_name))
134+
root=not cls.bases)
131135
if cls.ipa and cls.ipa.from_class is not None:
132136
source = cls.ipa.from_class
133137
get_ql_ipa_class_db(source).subtract_type(cls.name)
@@ -253,12 +257,14 @@ def generate(opts, renderer):
253257
existing |= {q for q in test_out.rglob("*.ql")}
254258
existing |= {q for q in test_out.rglob(missing_test_source_filename)}
255259

256-
data = schema.load(input)
260+
data = schema.load_file(input)
257261

258262
classes = {name: get_ql_class(cls, data.classes) for name, cls in data.classes.items()}
259-
# element root is absent in tests
260-
if schema.root_class_name in classes and classes[schema.root_class_name].has_children:
261-
raise RootElementHasChildren
263+
if not classes:
264+
raise NoClasses
265+
root = next(iter(classes.values()))
266+
if root.has_children:
267+
raise RootElementHasChildren(root)
262268

263269
imports = {}
264270

@@ -288,10 +294,10 @@ def generate(opts, renderer):
288294
for c in data.classes.values():
289295
if _should_skip_qltest(c, data.classes):
290296
continue
291-
test_dir = test_out / c.dir / c.name
297+
test_dir = test_out / c.group / c.name
292298
test_dir.mkdir(parents=True, exist_ok=True)
293299
if not any(test_dir.glob("*.swift")):
294-
log.warning(f"no test source in {c.dir / c.name}")
300+
log.warning(f"no test source in {test_dir.relative_to(test_out)}")
295301
renderer.render(ql.MissingTestInstructions(),
296302
test_dir / missing_test_source_filename)
297303
continue
@@ -308,12 +314,12 @@ def generate(opts, renderer):
308314
constructor_imports = []
309315
ipa_constructor_imports = []
310316
stubs = {}
311-
for cls in sorted(data.classes.values(), key=lambda cls: (cls.dir, cls.name)):
317+
for cls in sorted(data.classes.values(), key=lambda cls: (cls.group, cls.name)):
312318
ipa_type = get_ql_ipa_class(cls)
313319
if ipa_type.is_final:
314320
final_ipa_types.append(ipa_type)
315321
if ipa_type.has_params:
316-
stub_file = stub_out / cls.dir / f"{cls.name}Constructor.qll"
322+
stub_file = stub_out / cls.group / f"{cls.name}Constructor.qll"
317323
if not stub_file.is_file() or _is_generated_stub(stub_file):
318324
# stub rendering must be postponed as we might not have yet all subtracted ipa types in `ipa_type`
319325
stubs[stub_file] = ql.Synth.ConstructorStub(ipa_type)
@@ -326,7 +332,7 @@ def generate(opts, renderer):
326332

327333
for stub_file, data in stubs.items():
328334
renderer.render(data, stub_file)
329-
renderer.render(ql.Synth.Types(schema.root_class_name, final_ipa_types, non_final_ipa_types), out / "Synth.qll")
335+
renderer.render(ql.Synth.Types(root.name, final_ipa_types, non_final_ipa_types), out / "Synth.qll")
330336
renderer.render(ql.ImportList(constructor_imports), out / "SynthConstructors.qll")
331337
renderer.render(ql.ImportList(ipa_constructor_imports), out / "PureSynthConstructors.qll")
332338

swift/codegen/lib/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ load("@swift_codegen_deps//:requirements.bzl", "requirement")
22

33
py_library(
44
name = "lib",
5-
srcs = glob(["*.py"]),
5+
srcs = glob(["**/*.py"]),
66
visibility = ["//swift/codegen:__subpackages__"],
77
deps = [
88
requirement("pystache"),

swift/codegen/lib/schema.py

Lines changed: 0 additions & 184 deletions
This file was deleted.

swift/codegen/lib/schema/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .schema import *

0 commit comments

Comments
 (0)