Skip to content

Commit

Permalink
Merge pull request #9 from vkottler/dev/struct-sizes
Browse files Browse the repository at this point in the history
Initial type-system integration
  • Loading branch information
vkottler authored Aug 29, 2023
2 parents 5f76fb6 + 704efcb commit 24628f2
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
- run: |
mk python-release owner=vkottler \
repo=ifgen version=1.0.0
repo=ifgen version=1.1.0
if: |
matrix.python-version == '3.11'
&& matrix.system == 'ubuntu-latest'
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.3
hash=839acebe38039a168ae7975ef1b82408
hash=f027a094d4c73ea1fc44e8d63edcf8db
=====================================
-->

# ifgen ([1.0.0](https://pypi.org/project/ifgen/))
# ifgen ([1.1.0](https://pypi.org/project/ifgen/))

[![python](https://img.shields.io/pypi/pyversions/ifgen.svg)](https://pypi.org/project/ifgen/)
![Build Status](https://github.com/vkottler/ifgen/workflows/Python%20Package/badge.svg)
Expand Down
4 changes: 2 additions & 2 deletions ifgen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.3
# hash=7d1b42791b9f25d5e70560816ca96765
# hash=98bf88c49dfee15d176169be4c894053
# =====================================

"""
Expand All @@ -10,4 +10,4 @@

DESCRIPTION = "An interface generator for distributed computing."
PKG_NAME = "ifgen"
VERSION = "1.0.0"
VERSION = "1.1.0"
19 changes: 3 additions & 16 deletions ifgen/enum/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@

# internal
from ifgen.generation.interface import GenerateTask
from ifgen.generation.test import unit_test_boilerplate


def unit_test_body(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Implement a simple unit test for the enumeration."""

writer.write(f"using namespace {task.namespace()};")

writer.empty()

for enum in task.instance.get("enum", {}):
to_string = f"to_string({task.name}::{enum})"
writer.write(f"std::cout << {to_string} << std::endl;")
Expand All @@ -26,15 +23,5 @@ def unit_test_body(task: GenerateTask, writer: IndentedFileWriter) -> None:
def create_enum_test(task: GenerateTask) -> None:
"""Create a unit test for the enum string-conversion methods."""

include = task.env.rel_include(task.name, task.generator)

with task.boilerplate(
includes=["<cassert>", "<cstring>", "<iostream>", f'"{include}"'],
is_test=True,
use_namespace=False,
description=f"A unit test for {task.generator} {task.name}.",
) as writer:
writer.write("int main(void)")
with writer.scope():
unit_test_body(task, writer)
writer.write("return 0;")
with unit_test_boilerplate(task) as writer:
unit_test_body(task, writer)
74 changes: 64 additions & 10 deletions ifgen/environment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
# built-in
from enum import StrEnum
from pathlib import Path
from typing import Any

# third-party
from runtimepy.codec.system import TypeSystem
from vcorelib.logging import LoggerMixin
from vcorelib.namespace import CPP_DELIM, Namespace
from vcorelib.paths import normalize, rel

# internal
Expand All @@ -23,6 +24,31 @@ class Generator(StrEnum):
ENUMS = "enums"


def runtime_enum_data(data: dict[str, Any]) -> dict[str, int]:
"""Get runtime enumeration data."""

result = {}

curr_value = 0

for key, value in data.items():
if value is None or "value" not in value:
result[key] = curr_value
curr_value += 1
else:
result[key] = value["value"]
if value["value"] >= curr_value:
curr_value = value["value"] + 1

return result


def type_string(data: str) -> str:
"""Handle some type name conversions."""

return data.replace("_t", "")


class IfgenEnvironment(LoggerMixin):
"""A class for managing stateful information while generating outputs."""

Expand All @@ -48,15 +74,43 @@ def __init__(self, root: Path, config: Config) -> None:
for path in [self.output, self.test_dir]:
path.joinpath(subdir).mkdir(parents=True, exist_ok=True)

global_namespace = Namespace(delim=CPP_DELIM)

# Register global names.

self.root_namespace = global_namespace.child(
*self.config.data["namespace"]
)

# Register custom names for each generator.
self.types = TypeSystem(*self.config.data["namespace"])
self._register_enums()
self._register_structs()

def _register_enums(self) -> None:
"""Register configuration enums."""

for name, enum in self.config.data.get("enums", {}).items():
self.types.enum(
name,
runtime_enum_data(enum["enum"]),
*enum["namespace"],
primitive=type_string(enum["underlying"]),
)

self.logger.info(
"Registered enum '%s'.",
self.types.root_namespace.delim.join(
enum["namespace"] + [name]
),
)

def _register_structs(self) -> None:
"""Register configuration structs."""

for name, struct in self.config.data.get("structs", {}).items():
self.types.register(name, *struct["namespace"])
for field in struct["fields"]:
self.types.add(name, field["name"], type_string(field["type"]))

self.logger.info(
"Registered struct '%s' (%d bytes).",
self.types.root_namespace.delim.join(
struct["namespace"] + [name]
),
self.types.size(name, *struct["namespace"]),
)

def make_path(
self, name: str, generator: Generator, from_output: bool = False
Expand Down
4 changes: 2 additions & 2 deletions ifgen/generation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
from ifgen.enum import create_enum, create_enum_test
from ifgen.environment import Generator, IfgenEnvironment
from ifgen.generation.interface import GenerateTask, InstanceGenerator
from ifgen.struct import create_struct
from ifgen.struct import create_struct, create_struct_test

GENERATORS: Dict[Generator, List[InstanceGenerator]] = {
Generator.STRUCTS: [create_struct],
Generator.STRUCTS: [create_struct, create_struct_test],
Generator.ENUMS: [create_enum, create_enum_test],
}

Expand Down
2 changes: 1 addition & 1 deletion ifgen/generation/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def config(self) -> IfgenConfig:
def namespace(self) -> str:
"""Get this task's namespace."""

nspace = self.env.root_namespace
nspace = self.env.types.root_namespace
with nspace.pushed(*self.instance.get("namespace", [])):
result = nspace.namespace(track=False)

Expand Down
39 changes: 39 additions & 0 deletions ifgen/generation/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
A module implementing unit-testing related generation utilities.
"""

# built-in
from contextlib import contextmanager
from typing import Iterator, List

# third-party
from vcorelib.io import IndentedFileWriter

# internal
from ifgen.generation.interface import GenerateTask


@contextmanager
def unit_test_boilerplate(
task: GenerateTask, includes: List[str] = None
) -> Iterator[IndentedFileWriter]:
"""Handle standard unit-test boilerplate."""

include = task.env.rel_include(task.name, task.generator)

if includes is None:
includes = []

with task.boilerplate(
includes=["<cassert>", "<cstring>", "<iostream>", f'"{include}"']
+ includes,
is_test=True,
use_namespace=False,
description=f"A unit test for {task.generator} {task.name}.",
) as writer:
writer.write("int main(void)")
with writer.scope():
writer.write(f"using namespace {task.namespace()};")
writer.empty()
yield writer
writer.write("return 0;")
2 changes: 1 addition & 1 deletion ifgen/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
vcorelib>=2.5.2
runtimepy>=2.1.1
15 changes: 14 additions & 1 deletion ifgen/struct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

# internal
from ifgen.generation.interface import GenerateTask
from ifgen.struct.test import create_struct_test

__all__ = ["create_struct", "create_struct_test"]
FieldConfig = Dict[str, Union[int, str]]


Expand Down Expand Up @@ -62,7 +64,18 @@ def create_struct(task: GenerateTask) -> None:
"""Create a header file based on a struct definition."""

with task.boilerplate(includes=struct_includes(task), json=True) as writer:
writer.write(f"struct {task.name}")
attributes = ["gnu::packed"]
writer.write(f"struct [[{', '.join(attributes)}]] {task.name}")
with writer.scope(suffix=";"):
for field in task.instance["fields"]:
writer.write(struct_line(field.pop("name"), field))

writer.empty()

# Add size assertion.
writer.write(
(
f"static_assert(sizeof({task.name}) "
f"== {task.env.types.size(task.name)});"
)
)
26 changes: 26 additions & 0 deletions ifgen/struct/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
A module implementing a unit-test output generator for structs.
"""

# third-party
from vcorelib.io import IndentedFileWriter

# internal
from ifgen.generation.interface import GenerateTask
from ifgen.generation.test import unit_test_boilerplate


def unit_test_body(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Implement a unit test for a struct."""

del task

writer.cpp_comment("TODO.")
writer.empty()


def create_struct_test(task: GenerateTask) -> None:
"""Create a unit test for the enum string-conversion methods."""

with unit_test_boilerplate(task) as writer:
unit_test_body(task, writer)
2 changes: 1 addition & 1 deletion local/configs/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ ci_local:
- " if: matrix.system == 'ubuntu-latest'"

requirements:
- vcorelib>=2.5.2
- runtimepy>=2.1.1

dev_requirements:
- pytest-cov
Expand Down
2 changes: 1 addition & 1 deletion local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 1
minor: 0
minor: 1
patch: 0
entry: ig
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"

[project]
name = "ifgen"
version = "1.0.0"
version = "1.1.0"
description = "An interface generator for distributed computing."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion tests/data/valid/scenarios/sample/ifgen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ structs:
Test3:
fields:
- name: field1
type: int
type: int32_t

- name: field2
type: C::Test1
Expand Down

0 comments on commit 24628f2

Please sign in to comment.