Skip to content

Commit

Permalink
[compiler] adds json serialization to more types
Browse files Browse the repository at this point in the history
  • Loading branch information
thetheodor committed Sep 8, 2023
1 parent 3f159cf commit db9ad51
Show file tree
Hide file tree
Showing 3 changed files with 311 additions and 21 deletions.
234 changes: 230 additions & 4 deletions diopter/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,29 @@ def to_suffix(self) -> str:
case Language.CPP:
return ".cpp"

def to_json_dict(self) -> dict[str, Any]:
"""Returns a dictionary that can be serialized to json.
Returns:
dict[str, Any]:
the dictionary
"""
return {"name": self.name}

@staticmethod
def from_json_dict(d: dict[str, Any]) -> Language:
"""Returns a language parsed from a json dictionary.
Args:
d (dict[str, Any]):
the dictionary
Returns:
Language:
the language
"""
return Language[d["name"]]


@dataclass(frozen=True)
class SourcePath:
Expand All @@ -131,12 +154,10 @@ def __del__(self) -> None:

@dataclass(frozen=True, kw_only=True)
class Source(ABC):
"""A C or C++ base class for source programs together
"""A base class for C or C++source programs together
with flags, includes and macro definitions.
Attributes:
code (str):
the source code
language (Language):
the program's language
defined_macros (tuple[str,...]):
Expand Down Expand Up @@ -198,7 +219,122 @@ def get_file_suffix(self) -> str:

@abstractmethod
def get_filename(self) -> SourcePath:
pass
raise NotImplementedError

def to_json_dict(self) -> dict[str, Any]:
"""Returns a dictionary that can be serialized to json.
Can be re-parses with Source.from_json_dict.
Returns:
dict[str, Any]:
the dictionary
"""
j = {
"language": self.language.to_json_dict(),
"defined_macros": self.defined_macros,
"include_paths": self.include_paths,
"system_include_paths": self.system_include_paths,
"flags": self.flags,
}

j |= self.to_json_dict_impl()

assert set(j.keys()) == set(field.name for field in fields(self)) | set(
("kind",)
)

return j

@abstractmethod
def to_json_dict_impl(self) -> dict[str, Any]:
"""Returns a dictionary that can be serialized to json.
Subclasses should implement this method and serialize
their specific attributes.
Returns:
dict[str, Any]:
the dictionary
"""
raise NotImplementedError

@classmethod
def from_json_dict(cls, d: dict[str, Any]) -> Source:
"""Returns a source parsed from a json dictionary.
Args:
d (dict[str, Any]):
the dictionary
Returns:
Source:
the source
"""
if cls is Source:
match d["kind"]:
case "SourceFile":
return SourceFile.from_json_dict_impl(
d,
language=Language.from_json_dict(d["language"]),
defined_macros=tuple(d["defined_macros"]),
include_paths=tuple(d["include_paths"]),
system_include_paths=tuple(d["system_include_paths"]),
flags=tuple(d["flags"]),
)
case "SourceProgram":
return SourceProgram.from_json_dict_impl(
d,
language=Language.from_json_dict(d["language"]),
defined_macros=tuple(d["defined_macros"]),
include_paths=tuple(d["include_paths"]),
system_include_paths=tuple(d["system_include_paths"]),
flags=tuple(d["flags"]),
)

case _:
raise ValueError(f"Unknown kind: {d['kind']}")
else:
return cls.from_json_dict_impl(
d,
language=Language.from_json_dict(d["language"]),
defined_macros=tuple(d["defined_macros"]),
include_paths=tuple(d["include_paths"]),
system_include_paths=tuple(d["system_include_paths"]),
flags=tuple(d["flags"]),
)

@staticmethod
@abstractmethod
def from_json_dict_impl(
d: dict[str, Any],
language: Language,
defined_macros: tuple[str, ...],
include_paths: tuple[str, ...],
system_include_paths: tuple[str, ...],
flags: tuple[str, ...],
) -> Source:
"""Returns a source parsed from a json dictionary.
Subclasses should implement this and parse their
specific attributes from the dictionary.
Args:
d (dict[str, Any]):
the dictionary
language (Language):
the program's language
defined_macros (tuple[str,...]):
macros that will be defined when compiling this program
include_paths (tuple[str,...]):
include paths which will be passed to the compiler (with -I)
system_include_paths (tuple[str,...]):
system include paths which will be passed to the compiler (with -isystem)
flags (tuple[str,...]):
flags, prefixed with a dash ("-") that will be passed to the compiler
"""
raise NotImplementedError


ProgramType = TypeVar("ProgramType", bound="SourceProgram")
Expand Down Expand Up @@ -274,6 +410,51 @@ def with_code(self: ProgramType, new_code: str) -> ProgramType:
"""
return replace(self, code=new_code)

@staticmethod
def from_json_dict_impl(
d: dict[str, Any],
language: Language,
defined_macros: tuple[str, ...],
include_paths: tuple[str, ...],
system_include_paths: tuple[str, ...],
flags: tuple[str, ...],
) -> SourceProgram:
"""Returns a source program parsed from a json dictionary.
Args:
d (dict[str, Any]):
the dictionary
language (Language):
the program's language
defined_macros (tuple[str,...]):
macros that will be defined when compiling this program
include_paths (tuple[str,...]):
include paths which will be passed to the compiler (with -I)
system_include_paths (tuple[str,...]):
system include paths which will be passed to the compiler (with -isystem)
flags (tuple[str,...]):
flags, prefixed with a dash ("-") that will be passed to the compiler
"""
assert d["kind"] == "SourceProgram"
return SourceProgram(
code=d["code"],
language=language,
defined_macros=defined_macros,
include_paths=include_paths,
system_include_paths=system_include_paths,
flags=flags,
)

def to_json_dict_impl(self) -> dict[str, Any]:
"""Returns a dictionary representation of this source program,
only including the SourceProgram specific attributes.
Returns:
dict[str, Any]:
the dictionary
"""
return {"kind": "SourceProgram", "code": self.code}


@dataclass(frozen=True, kw_only=True)
class SourceFile(Source):
Expand Down Expand Up @@ -304,6 +485,51 @@ def __post_init__(self) -> None:
def get_filename(self) -> SourcePath:
return SourcePath(self.filename, None)

@staticmethod
def from_json_dict_impl(
d: dict[str, Any],
language: Language,
defined_macros: tuple[str, ...],
include_paths: tuple[str, ...],
system_include_paths: tuple[str, ...],
flags: tuple[str, ...],
) -> SourceFile:
"""Returns a source file parsed from a json dictionary.
Args:
d (dict[str, Any]):
the dictionary
language (Language):
the program's language
defined_macros (tuple[str,...]):
macros that will be defined when compiling this program
include_paths (tuple[str,...]):
include paths which will be passed to the compiler (with -I)
system_include_paths (tuple[str,...]):
system include paths which will be passed to the compiler (with -isystem)
flags (tuple[str,...]):
flags, prefixed with a dash ("-") that will be passed to the compiler
"""
assert d["kind"] == "SourceFile"
return SourceFile(
filename=Path(d["filename"]),
language=language,
defined_macros=defined_macros,
include_paths=include_paths,
system_include_paths=system_include_paths,
flags=flags,
)

def to_json_dict_impl(self) -> dict[str, Any]:
"""Returns a dictionary representation of this source program,
only including the SourceFile specific attributes.
Returns:
dict[str, Any]:
the dictionary
"""
return {"kind": "SourceFile", "filename": self.filename}


Revision = str

Expand Down
81 changes: 81 additions & 0 deletions tests/compiler_serialization_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from pathlib import Path

import pytest

from diopter.compiler import (
CompilationSetting,
CompilerExe,
CompilerProject,
Language,
OptLevel,
Source,
SourceFile,
SourceProgram,
)


def test_setting_serialization() -> None:
setting0 = CompilationSetting(
compiler=CompilerExe(CompilerProject.GCC, Path("gcc"), "test"),
opt_level=OptLevel.O2,
include_paths=("a", "b"),
system_include_paths=("sa", "sb"),
macro_definitions=("M1",),
)
setting1 = CompilationSetting(
compiler=CompilerExe(CompilerProject.LLVM, Path("llvm"), "test"),
opt_level=OptLevel.O0,
system_include_paths=("sb",),
)
assert setting0 == CompilationSetting.from_json_dict(setting0.to_json_dict())
assert setting1 == CompilationSetting.from_json_dict(setting1.to_json_dict())


def test_sourceprogram_serialization() -> None:
p0 = SourceProgram(
language=Language.C,
defined_macros=("M1", "M2"),
include_paths=("a", "b"),
system_include_paths=("sa", "sb"),
flags=("-fPIC", "-fno-omit-frame-pointer"),
code="bla bla",
)

assert p0 == SourceProgram.from_json_dict(p0.to_json_dict())
assert p0 == Source.from_json_dict(p0.to_json_dict())

p1 = SourceProgram(
language=Language.CPP,
defined_macros=("M1",),
system_include_paths=("sa",),
code="bla bla blac",
)

assert p1 == SourceProgram.from_json_dict(p1.to_json_dict())
assert p1 == Source.from_json_dict(p1.to_json_dict())

with pytest.raises(AssertionError):
SourceFile.from_json_dict(p1.to_json_dict())


def test_sourcefile_serialization() -> None:
p0 = SourceFile(
language=Language.C,
filename=Path("/bla/bla"),
)

assert p0 == SourceFile.from_json_dict(p0.to_json_dict())
assert p0 == Source.from_json_dict(p0.to_json_dict())

p1 = SourceFile(
language=Language.CPP,
defined_macros=("M1",),
system_include_paths=("sa",),
filename=Path("bla/bla/blac"),
)

assert p1 == SourceFile.from_json_dict(p1.to_json_dict())
assert p1 == Source.from_json_dict(p1.to_json_dict())

with pytest.raises(AssertionError):
SourceProgram.from_json_dict(p1.to_json_dict())
17 changes: 0 additions & 17 deletions tests/compiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,20 +287,3 @@ def test_opt() -> None:
opt = get_opt()
opt_res = opt.run_on_input(res.output.filename, ("-S",))
assert llvm_ir.strip() != opt_res.stdout.strip()


def test_serialization() -> None:
setting0 = CompilationSetting(
compiler=CompilerExe(CompilerProject.GCC, Path("gcc"), "test"),
opt_level=OptLevel.O2,
include_paths=("a", "b"),
system_include_paths=("sa", "sb"),
macro_definitions=("M1",),
)
setting1 = CompilationSetting(
compiler=CompilerExe(CompilerProject.LLVM, Path("llvm"), "test"),
opt_level=OptLevel.O0,
system_include_paths=("sb",),
)
assert setting0 == CompilationSetting.from_json_dict(setting0.to_json_dict())
assert setting1 == CompilationSetting.from_json_dict(setting1.to_json_dict())

0 comments on commit db9ad51

Please sign in to comment.