From 3267c881666e6c044e7ea79ecff5b3441ac246a5 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sun, 15 Oct 2023 21:06:40 -0500 Subject: [PATCH 1/4] 3.1.0 - Re-structure for more bit-field methods --- .github/workflows/python-package.yml | 2 +- README.md | 4 +- ifgen/__init__.py | 4 +- ifgen/struct/methods/fields.py | 232 ------------------------ ifgen/struct/methods/fields/__init__.py | 137 ++++++++++++++ ifgen/struct/methods/fields/common.py | 24 +++ ifgen/struct/methods/fields/getter.py | 73 ++++++++ ifgen/struct/methods/fields/setter.py | 77 ++++++++ local/variables/package.yaml | 4 +- pyproject.toml | 2 +- 10 files changed, 319 insertions(+), 240 deletions(-) delete mode 100644 ifgen/struct/methods/fields.py create mode 100644 ifgen/struct/methods/fields/__init__.py create mode 100644 ifgen/struct/methods/fields/common.py create mode 100644 ifgen/struct/methods/fields/getter.py create mode 100644 ifgen/struct/methods/fields/setter.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 28c8574..a755b40 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -77,7 +77,7 @@ jobs: - run: | mk python-release owner=vkottler \ - repo=ifgen version=3.0.1 + repo=ifgen version=3.1.0 if: | matrix.python-version == '3.11' && matrix.system == 'ubuntu-latest' diff --git a/README.md b/README.md index 11771bf..dbbd28b 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ ===================================== generator=datazen version=3.1.4 - hash=e2d67bfd7efcf9a8fd101e9a24006e5a + hash=81700982f3f009c9782a7d73261925b4 ===================================== --> -# ifgen ([3.0.1](https://pypi.org/project/ifgen/)) +# ifgen ([3.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) diff --git a/ifgen/__init__.py b/ifgen/__init__.py index b8037ff..d9a0b93 100644 --- a/ifgen/__init__.py +++ b/ifgen/__init__.py @@ -1,7 +1,7 @@ # ===================================== # generator=datazen # version=3.1.4 -# hash=75a8be659b7a0b9eac6bb40ac4061a5a +# hash=addf6d9c81cc0f844ae7e2c96f5f4bb4 # ===================================== """ @@ -10,4 +10,4 @@ DESCRIPTION = "An interface generator for distributed computing." PKG_NAME = "ifgen" -VERSION = "3.0.1" +VERSION = "3.1.0" diff --git a/ifgen/struct/methods/fields.py b/ifgen/struct/methods/fields.py deleted file mode 100644 index e9e49cc..0000000 --- a/ifgen/struct/methods/fields.py +++ /dev/null @@ -1,232 +0,0 @@ -""" -A module implementing an interface for generating bit-field methods for -structs. -""" - -# built-in -from contextlib import ExitStack -from typing import Any, Optional - -# third-party -from vcorelib.io.file_writer import IndentedFileWriter - -# internal -from ifgen.generation.interface import GenerateTask -from ifgen.struct.methods.bit import bit_field_toggle_method - -STANDARD_INTS = [ - ("uint8_t", 8), - ("uint16_t", 16), - ("uint32_t", 32), - ("uint64_t", 64), -] - - -def bit_field_underlying(field: dict[str, Any]) -> str: - """Get the underlying type for a bit field.""" - - kind = field.get("type") - - # Automatically determine a sane primitive-integer type to use if one isn't - # specified. - if kind is None: - width = field["width"] - - if width == 1: - kind = "bool" - else: - for candidate, bit_width in STANDARD_INTS: - if field["width"] <= bit_width: - kind = candidate - break - - assert kind is not None, kind - return kind - - -def possible_array_arg(parent: dict[str, Any]) -> str: - """Determine if a method needs an array-index argument.""" - - array_length: Optional[int] = parent.get("array_length") - inner = "" - if array_length: - inner = "std::size_t index" - - return inner - - -def bit_field_get_method( - task: GenerateTask, - parent: dict[str, Any], - field: dict[str, Any], - writer: IndentedFileWriter, - header: bool, - kind: str, - method_slug: str, - alias: str = None, -) -> None: - """Generate a 'get' method for a bit-field.""" - - if not header: - return - - is_flag = field["width"] == 1 - - inner = possible_array_arg(parent) - - method = task.cpp_namespace(f"get_{method_slug}({inner})", header=header) - writer.empty() - - with writer.javadoc(): - writer.write( - ( - f"Get {parent['name']}'s {field['name']} " - f"{'field' if field['width'] > 1 else 'bit'}." - ) - ) - - line = f"{kind} " + method - - lhs = parent["name"] if not alias else alias - if inner: - lhs += "[index]" - - with ExitStack() as stack: - if is_flag: - writer.write(line) - stack.enter_context(writer.scope()) - stmt = f"{lhs} & (1u << {field['index']}u)" - else: - writer.write(line) - stack.enter_context(writer.scope()) - stmt = ( - f"({lhs} >> {field['index']}u) & " - f"{bit_mask_literal(field['width'])}" - ) - - if task.env.is_enum(kind): - stmt = f"{kind}({stmt})" - - writer.write(f"return {stmt};") - - -def bit_mask_literal(width: int) -> str: - """Get a bit-mask literal.""" - return "0b" + ("1" * width) + "u" - - -def bit_field_set_method( - task: GenerateTask, - parent: dict[str, Any], - field: dict[str, Any], - writer: IndentedFileWriter, - header: bool, - kind: str, - method_slug: str, - alias: str = None, -) -> None: - """Generate a 'set' method for a bit-field.""" - - # Generate a toggle method for bit fields. - if field["width"] == 1: - bit_field_toggle_method( - task, parent["name"], field, writer, header, method_slug - ) - else: - if not header: - return - - inner = possible_array_arg(parent) - if inner: - inner += ", " - inner += f"{kind} value" - - method = task.cpp_namespace( - f"set_{method_slug}({inner})", header=header - ) - writer.empty() - - if header: - with writer.javadoc(): - writer.write(f"Set {parent['name']}'s {field['name']} field.") - - writer.write("inline void " + method) - with writer.scope(): - rhs = parent["name"] if not alias else alias - if "index" in inner: - rhs += "[index]" - - writer.write(f"{parent['type']} curr = {rhs};") - - mask = bit_mask_literal(field["width"]) - - with writer.padding(): - writer.write(f"curr &= ~({mask} << {field['index']}u);") - - val_str = "value" - if task.env.is_enum(kind): - val_str = f"std::to_underlying({val_str})" - - writer.write( - f"curr |= ({val_str} & {mask}) << {field['index']}u;" - ) - - writer.write(f"{rhs} = curr;") - - -def bit_field( - task: GenerateTask, - parent: dict[str, Any], - field: dict[str, Any], - writer: IndentedFileWriter, - header: bool, - alias: str = None, -) -> None: - """Generate for an individual bit-field.""" - - kind = bit_field_underlying(field) - - type_size = task.env.size(parent["type"]) * 8 - - index = field["index"] - width = field["width"] - - # Validate field parameters. - assert index + width <= type_size, (index, width, type_size, field) - assert field["read"] or field["write"], field - - name = parent["name"] if not alias else alias - method_slug = f"{name}_{field['name']}" - - # Generate a 'get' method. - if field["read"]: - bit_field_get_method( - task, parent, field, writer, header, kind, method_slug, alias=alias - ) - - # Generate a 'set' method. - if field["write"]: - bit_field_set_method( - task, parent, field, writer, header, kind, method_slug, alias=alias - ) - - -def bit_fields( - task: GenerateTask, writer: IndentedFileWriter, header: bool -) -> None: - """Generate bit-field lines.""" - - for field in task.instance["fields"]: - for bfield in field.get("fields", []): - bit_field(task, field, bfield, writer, header) - - for alternate in field.get("alternates", []): - for bfield in alternate.get("fields", []): - bit_field( - task, - field, - bfield, - writer, - header, - alias=alternate["name"], - ) diff --git a/ifgen/struct/methods/fields/__init__.py b/ifgen/struct/methods/fields/__init__.py new file mode 100644 index 0000000..5ef8e7c --- /dev/null +++ b/ifgen/struct/methods/fields/__init__.py @@ -0,0 +1,137 @@ +""" +A module implementing an interface for generating bit-field methods for +structs. +""" + +# built-in +from typing import Any + +# third-party +from vcorelib.io.file_writer import IndentedFileWriter + +# internal +from ifgen.generation.interface import GenerateTask +from ifgen.struct.methods.fields.common import BitField +from ifgen.struct.methods.fields.getter import bit_field_get_method +from ifgen.struct.methods.fields.setter import bit_field_set_method + +STANDARD_INTS = [ + ("uint8_t", 8), + ("uint16_t", 16), + ("uint32_t", 32), + ("uint64_t", 64), +] + + +def bit_field_underlying(field: dict[str, Any]) -> str: + """Get the underlying type for a bit field.""" + + kind = field.get("type") + + # Automatically determine a sane primitive-integer type to use if one isn't + # specified. + if kind is None: + width = field["width"] + + if width == 1: + kind = "bool" + else: + for candidate, bit_width in STANDARD_INTS: + if field["width"] <= bit_width: + kind = candidate + break + + assert kind is not None, kind + return kind + + +def bit_field( + task: GenerateTask, + parent: dict[str, Any], + field: BitField, + writer: IndentedFileWriter, + header: bool, + read_fields: list[BitField], + write_fields: list[BitField], + alias: str = None, +) -> None: + """Generate for an individual bit-field.""" + + kind = bit_field_underlying(field) + + type_size = task.env.size(parent["type"]) * 8 + + index = field["index"] + width = field["width"] + + # Validate field parameters. + assert index + width <= type_size, (index, width, type_size, field) + assert field["read"] or field["write"], field + + name = parent["name"] if not alias else alias + method_slug = f"{name}_{field['name']}" + + # Generate a 'get' method. + if field["read"]: + bit_field_get_method( + task, parent, field, writer, header, kind, method_slug, alias=alias + ) + read_fields.append(field) + + # Generate a 'set' method. + if field["write"]: + bit_field_set_method( + task, parent, field, writer, header, kind, method_slug, alias=alias + ) + write_fields.append(field) + + +def handle_atomic_fields_methods( + read_fields: list[BitField], + write_fields: list[BitField], + alias: str = None, +) -> None: + """Handle additional bit-field methods.""" + + print(alias) + + if len(read_fields) > 1: + print(read_fields) + + if len(write_fields) > 1: + print(write_fields) + + +def bit_fields( + task: GenerateTask, writer: IndentedFileWriter, header: bool +) -> None: + """Generate bit-field lines.""" + + for field in task.instance["fields"]: + read_fields: list[BitField] = [] + write_fields: list[BitField] = [] + + for bfield in field.get("fields", []): + bit_field( + task, field, bfield, writer, header, read_fields, write_fields + ) + handle_atomic_fields_methods(read_fields, write_fields) + + for alternate in field.get("alternates", []): + read_fields = [] + write_fields = [] + + for bfield in alternate.get("fields", []): + bit_field( + task, + field, + bfield, + writer, + header, + read_fields, + write_fields, + alias=alternate["name"], + ) + handle_atomic_fields_methods( + read_fields, write_fields, alias=alternate["name"] + ) diff --git a/ifgen/struct/methods/fields/common.py b/ifgen/struct/methods/fields/common.py new file mode 100644 index 0000000..d1e96de --- /dev/null +++ b/ifgen/struct/methods/fields/common.py @@ -0,0 +1,24 @@ +""" +Common utilities for generating bit-field related struct methods. +""" + +# built-in +from typing import Any, Optional + +BitField = dict[str, Any] + + +def bit_mask_literal(width: int) -> str: + """Get a bit-mask literal.""" + return "0b" + ("1" * width) + "u" + + +def possible_array_arg(parent: dict[str, Any]) -> str: + """Determine if a method needs an array-index argument.""" + + array_length: Optional[int] = parent.get("array_length") + inner = "" + if array_length: + inner = "std::size_t index" + + return inner diff --git a/ifgen/struct/methods/fields/getter.py b/ifgen/struct/methods/fields/getter.py new file mode 100644 index 0000000..19b18d1 --- /dev/null +++ b/ifgen/struct/methods/fields/getter.py @@ -0,0 +1,73 @@ +""" +A module implementing 'get' methods for bit-fields. +""" + +# built-in +from contextlib import ExitStack +from typing import Any + +# third-party +from vcorelib.io.file_writer import IndentedFileWriter + +# internal +from ifgen.generation.interface import GenerateTask +from ifgen.struct.methods.fields.common import ( + BitField, + bit_mask_literal, + possible_array_arg, +) + + +def bit_field_get_method( + task: GenerateTask, + parent: dict[str, Any], + field: BitField, + writer: IndentedFileWriter, + header: bool, + kind: str, + method_slug: str, + alias: str = None, +) -> None: + """Generate a 'get' method for a bit-field.""" + + if not header: + return + + is_flag = field["width"] == 1 + + inner = possible_array_arg(parent) + + method = task.cpp_namespace(f"get_{method_slug}({inner})", header=header) + writer.empty() + + with writer.javadoc(): + writer.write( + ( + f"Get {parent['name']}'s {field['name']} " + f"{'field' if field['width'] > 1 else 'bit'}." + ) + ) + + line = f"{kind} " + method + + lhs = parent["name"] if not alias else alias + if inner: + lhs += "[index]" + + with ExitStack() as stack: + if is_flag: + writer.write(line) + stack.enter_context(writer.scope()) + stmt = f"{lhs} & (1u << {field['index']}u)" + else: + writer.write(line) + stack.enter_context(writer.scope()) + stmt = ( + f"({lhs} >> {field['index']}u) & " + f"{bit_mask_literal(field['width'])}" + ) + + if task.env.is_enum(kind): + stmt = f"{kind}({stmt})" + + writer.write(f"return {stmt};") diff --git a/ifgen/struct/methods/fields/setter.py b/ifgen/struct/methods/fields/setter.py new file mode 100644 index 0000000..1c634bc --- /dev/null +++ b/ifgen/struct/methods/fields/setter.py @@ -0,0 +1,77 @@ +""" +A module implementing 'set' methods for bit-fields. +""" + +# built-in +from typing import Any + +# third-party +from vcorelib.io.file_writer import IndentedFileWriter + +# internal +from ifgen.generation.interface import GenerateTask +from ifgen.struct.methods.bit import bit_field_toggle_method +from ifgen.struct.methods.fields.common import ( + BitField, + bit_mask_literal, + possible_array_arg, +) + + +def bit_field_set_method( + task: GenerateTask, + parent: dict[str, Any], + field: BitField, + writer: IndentedFileWriter, + header: bool, + kind: str, + method_slug: str, + alias: str = None, +) -> None: + """Generate a 'set' method for a bit-field.""" + + # Generate a toggle method for bit fields. + if field["width"] == 1: + bit_field_toggle_method( + task, parent["name"], field, writer, header, method_slug + ) + else: + if not header: + return + + inner = possible_array_arg(parent) + if inner: + inner += ", " + inner += f"{kind} value" + + method = task.cpp_namespace( + f"set_{method_slug}({inner})", header=header + ) + writer.empty() + + if header: + with writer.javadoc(): + writer.write(f"Set {parent['name']}'s {field['name']} field.") + + writer.write("inline void " + method) + with writer.scope(): + rhs = parent["name"] if not alias else alias + if "index" in inner: + rhs += "[index]" + + writer.write(f"{parent['type']} curr = {rhs};") + + mask = bit_mask_literal(field["width"]) + + with writer.padding(): + writer.write(f"curr &= ~({mask} << {field['index']}u);") + + val_str = "value" + if task.env.is_enum(kind): + val_str = f"std::to_underlying({val_str})" + + writer.write( + f"curr |= ({val_str} & {mask}) << {field['index']}u;" + ) + + writer.write(f"{rhs} = curr;") diff --git a/local/variables/package.yaml b/local/variables/package.yaml index aa7d92e..9e02b3f 100644 --- a/local/variables/package.yaml +++ b/local/variables/package.yaml @@ -1,5 +1,5 @@ --- major: 3 -minor: 0 -patch: 1 +minor: 1 +patch: 0 entry: ig diff --git a/pyproject.toml b/pyproject.toml index 793e00c..a9e648a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__" [project] name = "ifgen" -version = "3.0.1" +version = "3.1.0" description = "An interface generator for distributed computing." readme = "README.md" requires-python = ">=3.11" From ffd03f687738a12ff4fb9ee31b32782ddae63d4d Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sun, 15 Oct 2023 21:22:24 -0500 Subject: [PATCH 2/4] Getting closer to generation --- ifgen/struct/methods/fields/__init__.py | 38 +++++++++++++++++++------ ifgen/struct/methods/fields/getter.py | 21 ++++++++++++++ ifgen/struct/methods/fields/setter.py | 21 ++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/ifgen/struct/methods/fields/__init__.py b/ifgen/struct/methods/fields/__init__.py index 5ef8e7c..56a0480 100644 --- a/ifgen/struct/methods/fields/__init__.py +++ b/ifgen/struct/methods/fields/__init__.py @@ -12,8 +12,14 @@ # internal from ifgen.generation.interface import GenerateTask from ifgen.struct.methods.fields.common import BitField -from ifgen.struct.methods.fields.getter import bit_field_get_method -from ifgen.struct.methods.fields.setter import bit_field_set_method +from ifgen.struct.methods.fields.getter import ( + bit_field_get_all_method, + bit_field_get_method, +) +from ifgen.struct.methods.fields.setter import ( + bit_field_set_all_method, + bit_field_set_method, +) STANDARD_INTS = [ ("uint8_t", 8), @@ -87,19 +93,27 @@ def bit_field( def handle_atomic_fields_methods( + task: GenerateTask, + writer: IndentedFileWriter, + header: bool, + field: dict[str, Any], read_fields: list[BitField], write_fields: list[BitField], alias: str = None, ) -> None: """Handle additional bit-field methods.""" - print(alias) - if len(read_fields) > 1: - print(read_fields) + writer.empty() + bit_field_get_all_method( + task, writer, header, field, read_fields, alias=alias + ) if len(write_fields) > 1: - print(write_fields) + writer.empty() + bit_field_set_all_method( + task, writer, header, field, write_fields, alias=alias + ) def bit_fields( @@ -115,7 +129,9 @@ def bit_fields( bit_field( task, field, bfield, writer, header, read_fields, write_fields ) - handle_atomic_fields_methods(read_fields, write_fields) + handle_atomic_fields_methods( + task, writer, header, field, read_fields, write_fields + ) for alternate in field.get("alternates", []): read_fields = [] @@ -133,5 +149,11 @@ def bit_fields( alias=alternate["name"], ) handle_atomic_fields_methods( - read_fields, write_fields, alias=alternate["name"] + task, + writer, + header, + field, + read_fields, + write_fields, + alias=alternate["name"], ) diff --git a/ifgen/struct/methods/fields/getter.py b/ifgen/struct/methods/fields/getter.py index 19b18d1..a8631d2 100644 --- a/ifgen/struct/methods/fields/getter.py +++ b/ifgen/struct/methods/fields/getter.py @@ -18,6 +18,27 @@ ) +def bit_field_get_all_method( + task: GenerateTask, + writer: IndentedFileWriter, + header: bool, + field: dict[str, Any], + fields: list[BitField], + alias: str = None, +) -> None: + """Generate a 'get' method for multiple bit-field.""" + + if not header: + return + + name = field["name"] if not alias else alias + writer.c_comment(f"get_{name}") + + print(task) + + print(fields) + + def bit_field_get_method( task: GenerateTask, parent: dict[str, Any], diff --git a/ifgen/struct/methods/fields/setter.py b/ifgen/struct/methods/fields/setter.py index 1c634bc..a59c369 100644 --- a/ifgen/struct/methods/fields/setter.py +++ b/ifgen/struct/methods/fields/setter.py @@ -18,6 +18,27 @@ ) +def bit_field_set_all_method( + task: GenerateTask, + writer: IndentedFileWriter, + header: bool, + field: dict[str, Any], + fields: list[BitField], + alias: str = None, +) -> None: + """Generate a 'set' method for multiple bit-field.""" + + if not header: + return + + name = field["name"] if not alias else alias + writer.c_comment(f"set_{name}") + + print(task) + + print(fields) + + def bit_field_set_method( task: GenerateTask, parent: dict[str, Any], From fb750a5e3306b4450d209c70fc39da2d4af3deef Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Mon, 16 Oct 2023 00:35:24 -0500 Subject: [PATCH 3/4] Finish set/get implementation --- ifgen/struct/methods/fields/__init__.py | 42 +------------ ifgen/struct/methods/fields/common.py | 41 ++++++++++++ ifgen/struct/methods/fields/getter.py | 83 +++++++++++++++++-------- ifgen/struct/methods/fields/setter.py | 72 ++++++++++++++++----- 4 files changed, 155 insertions(+), 83 deletions(-) diff --git a/ifgen/struct/methods/fields/__init__.py b/ifgen/struct/methods/fields/__init__.py index 56a0480..d6fff17 100644 --- a/ifgen/struct/methods/fields/__init__.py +++ b/ifgen/struct/methods/fields/__init__.py @@ -21,35 +21,6 @@ bit_field_set_method, ) -STANDARD_INTS = [ - ("uint8_t", 8), - ("uint16_t", 16), - ("uint32_t", 32), - ("uint64_t", 64), -] - - -def bit_field_underlying(field: dict[str, Any]) -> str: - """Get the underlying type for a bit field.""" - - kind = field.get("type") - - # Automatically determine a sane primitive-integer type to use if one isn't - # specified. - if kind is None: - width = field["width"] - - if width == 1: - kind = "bool" - else: - for candidate, bit_width in STANDARD_INTS: - if field["width"] <= bit_width: - kind = candidate - break - - assert kind is not None, kind - return kind - def bit_field( task: GenerateTask, @@ -63,8 +34,6 @@ def bit_field( ) -> None: """Generate for an individual bit-field.""" - kind = bit_field_underlying(field) - type_size = task.env.size(parent["type"]) * 8 index = field["index"] @@ -74,21 +43,14 @@ def bit_field( assert index + width <= type_size, (index, width, type_size, field) assert field["read"] or field["write"], field - name = parent["name"] if not alias else alias - method_slug = f"{name}_{field['name']}" - # Generate a 'get' method. if field["read"]: - bit_field_get_method( - task, parent, field, writer, header, kind, method_slug, alias=alias - ) + bit_field_get_method(task, parent, field, writer, header, alias=alias) read_fields.append(field) # Generate a 'set' method. if field["write"]: - bit_field_set_method( - task, parent, field, writer, header, kind, method_slug, alias=alias - ) + bit_field_set_method(task, parent, field, writer, header, alias=alias) write_fields.append(field) diff --git a/ifgen/struct/methods/fields/common.py b/ifgen/struct/methods/fields/common.py index d1e96de..08357d3 100644 --- a/ifgen/struct/methods/fields/common.py +++ b/ifgen/struct/methods/fields/common.py @@ -22,3 +22,44 @@ def possible_array_arg(parent: dict[str, Any]) -> str: inner = "std::size_t index" return inner + + +STANDARD_INTS = [ + ("uint8_t", 8), + ("uint16_t", 16), + ("uint32_t", 32), + ("uint64_t", 64), +] + + +def bit_field_underlying(field: dict[str, Any]) -> str: + """Get the underlying type for a bit field.""" + + kind = field.get("type") + + # Automatically determine a sane primitive-integer type to use if one isn't + # specified. + if kind is None: + width = field["width"] + + if width == 1: + kind = "bool" + else: + for candidate, bit_width in STANDARD_INTS: + if field["width"] <= bit_width: + kind = candidate + break + + assert kind is not None, kind + return kind + + +def bit_field_method_slug( + field: dict[str, Any], member: str = "", alias: str = None +) -> str: + """Get a method slug for a struct's bit-field method.""" + + name = str(field["name"]) if not alias else alias + if member: + name += "_" + member + return name diff --git a/ifgen/struct/methods/fields/getter.py b/ifgen/struct/methods/fields/getter.py index a8631d2..06ef4a9 100644 --- a/ifgen/struct/methods/fields/getter.py +++ b/ifgen/struct/methods/fields/getter.py @@ -3,7 +3,6 @@ """ # built-in -from contextlib import ExitStack from typing import Any # third-party @@ -13,6 +12,8 @@ from ifgen.generation.interface import GenerateTask from ifgen.struct.methods.fields.common import ( BitField, + bit_field_method_slug, + bit_field_underlying, bit_mask_literal, possible_array_arg, ) @@ -32,11 +33,58 @@ def bit_field_get_all_method( return name = field["name"] if not alias else alias - writer.c_comment(f"get_{name}") - print(task) + inner = possible_array_arg(field) + if inner: + inner += ", " + + with writer.javadoc(): + writer.write(f"Get all of {name}'s bit fields.") + + # Add field args. + args = [] + for bit_field in fields: + args.append(f"{bit_field_underlying(bit_field)} &{bit_field['name']}") + + inner += ", ".join(args) + + writer.write(f"inline void get_{name}({inner})") + with writer.scope(): + rhs = field["name"] if not alias else alias + if "index" in inner: + rhs += "[index]" - print(fields) + writer.write(f"{field['type']} curr = {rhs};") + writer.empty() + + for bit_field in fields: + stmt = get_bit_field_statement(task, bit_field, "curr") + writer.write(f"{bit_field['name']} = {stmt};") + + +def get_bit_field_statement( + task: GenerateTask, field: BitField, lhs: str +) -> str: + """ + Get the arithmetic statement associated with a bit-field get operation. + """ + + kind = bit_field_underlying(field) + + is_flag = field["width"] == 1 + + if is_flag: + stmt = f"{lhs} & (1u << {field['index']}u)" + else: + stmt = ( + f"({lhs} >> {field['index']}u) & " + f"{bit_mask_literal(field['width'])}" + ) + + if task.env.is_enum(kind): + stmt = f"{kind}({stmt})" + + return stmt def bit_field_get_method( @@ -45,8 +93,6 @@ def bit_field_get_method( field: BitField, writer: IndentedFileWriter, header: bool, - kind: str, - method_slug: str, alias: str = None, ) -> None: """Generate a 'get' method for a bit-field.""" @@ -54,10 +100,9 @@ def bit_field_get_method( if not header: return - is_flag = field["width"] == 1 - inner = possible_array_arg(parent) + method_slug = bit_field_method_slug(parent, field["name"], alias=alias) method = task.cpp_namespace(f"get_{method_slug}({inner})", header=header) writer.empty() @@ -69,26 +114,12 @@ def bit_field_get_method( ) ) - line = f"{kind} " + method + line = f"inline {bit_field_underlying(field)} " + method lhs = parent["name"] if not alias else alias if inner: lhs += "[index]" - with ExitStack() as stack: - if is_flag: - writer.write(line) - stack.enter_context(writer.scope()) - stmt = f"{lhs} & (1u << {field['index']}u)" - else: - writer.write(line) - stack.enter_context(writer.scope()) - stmt = ( - f"({lhs} >> {field['index']}u) & " - f"{bit_mask_literal(field['width'])}" - ) - - if task.env.is_enum(kind): - stmt = f"{kind}({stmt})" - - writer.write(f"return {stmt};") + writer.write(line) + with writer.scope(): + writer.write(f"return {get_bit_field_statement(task, field, lhs)};") diff --git a/ifgen/struct/methods/fields/setter.py b/ifgen/struct/methods/fields/setter.py index a59c369..d0b84d1 100644 --- a/ifgen/struct/methods/fields/setter.py +++ b/ifgen/struct/methods/fields/setter.py @@ -3,7 +3,7 @@ """ # built-in -from typing import Any +from typing import Any, Iterator # third-party from vcorelib.io.file_writer import IndentedFileWriter @@ -13,6 +13,8 @@ from ifgen.struct.methods.bit import bit_field_toggle_method from ifgen.struct.methods.fields.common import ( BitField, + bit_field_method_slug, + bit_field_underlying, bit_mask_literal, possible_array_arg, ) @@ -32,11 +34,55 @@ def bit_field_set_all_method( return name = field["name"] if not alias else alias - writer.c_comment(f"set_{name}") - print(task) + inner = possible_array_arg(field) + if inner: + inner += ", " - print(fields) + with writer.javadoc(): + writer.write(f"Set all of {name}'s bit fields.") + + # Add field args. + args = [] + for bit_field in fields: + args.append(f"{bit_field_underlying(bit_field)} {bit_field['name']}") + + inner += ", ".join(args) + writer.write(f"inline void set_{name}({inner})") + with writer.scope(): + rhs = field["name"] if not alias else alias + if "index" in inner: + rhs += "[index]" + + writer.write(f"{field['type']} curr = {rhs};") + + with writer.padding(): + for bit_field in fields: + for line in bit_field_set_lines( + task, bit_field, value=bit_field["name"] + ): + writer.write(line) + + writer.write(f"{rhs} = curr;") + + +def bit_field_set_lines( + task: GenerateTask, + field: BitField, + lhs: str = "curr", + value: str = "value", +) -> Iterator[str]: + """Get lines that perform a bit-field's assignment.""" + + mask = bit_mask_literal(field["width"]) + + yield f"{lhs} &= ~({mask} << {field['index']}u);" + + val_str = value + if task.env.is_enum(bit_field_underlying(field)): + val_str = f"std::to_underlying({val_str})" + + yield f"{lhs} |= ({val_str} & {mask}) << {field['index']}u;" def bit_field_set_method( @@ -45,12 +91,13 @@ def bit_field_set_method( field: BitField, writer: IndentedFileWriter, header: bool, - kind: str, - method_slug: str, alias: str = None, ) -> None: """Generate a 'set' method for a bit-field.""" + method_slug = bit_field_method_slug(parent, field["name"], alias=alias) + kind = bit_field_underlying(field) + # Generate a toggle method for bit fields. if field["width"] == 1: bit_field_toggle_method( @@ -82,17 +129,8 @@ def bit_field_set_method( writer.write(f"{parent['type']} curr = {rhs};") - mask = bit_mask_literal(field["width"]) - with writer.padding(): - writer.write(f"curr &= ~({mask} << {field['index']}u);") - - val_str = "value" - if task.env.is_enum(kind): - val_str = f"std::to_underlying({val_str})" - - writer.write( - f"curr |= ({val_str} & {mask}) << {field['index']}u;" - ) + for line in bit_field_set_lines(task, field): + writer.write(line) writer.write(f"{rhs} = curr;") From 7db46147defa46975ade1a795896c1a96e7a4893 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Mon, 16 Oct 2023 00:50:57 -0500 Subject: [PATCH 4/4] Don't generate identifiers for SVD outputs --- ifgen/data/schemas/Enum.yaml | 4 ++++ ifgen/data/schemas/Struct.yaml | 4 ++++ ifgen/enum/header.py | 5 ++++- ifgen/struct/__init__.py | 15 ++++++++------- ifgen/svd/group/fields.py | 2 ++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ifgen/data/schemas/Enum.yaml b/ifgen/data/schemas/Enum.yaml index 23d3e8d..40f2039 100644 --- a/ifgen/data/schemas/Enum.yaml +++ b/ifgen/data/schemas/Enum.yaml @@ -26,6 +26,10 @@ properties: type: boolean default: true + identifier: + type: boolean + default: true + underlying: type: string default: uint8_t diff --git a/ifgen/data/schemas/Struct.yaml b/ifgen/data/schemas/Struct.yaml index 8d6ca91..27c9826 100644 --- a/ifgen/data/schemas/Struct.yaml +++ b/ifgen/data/schemas/Struct.yaml @@ -21,6 +21,10 @@ properties: type: boolean default: true + identifier: + type: boolean + default: true + instances: type: array items: diff --git a/ifgen/enum/header.py b/ifgen/enum/header.py index 4dc1c5f..5896536 100644 --- a/ifgen/enum/header.py +++ b/ifgen/enum/header.py @@ -51,7 +51,9 @@ def enum_header(task: GenerateTask, writer: IndentedFileWriter) -> None: runtime = task.enum() - with writer.padding(): + writer.empty() + + if task.instance["identifier"]: writer.write( ( "static constexpr " @@ -59,6 +61,7 @@ def enum_header(task: GenerateTask, writer: IndentedFileWriter) -> None: f"{task.name}_id = {runtime.id};" ) ) + writer.empty() enum_to_string_function( task, writer, task.instance["use_map"], definition=True diff --git a/ifgen/struct/__init__.py b/ifgen/struct/__init__.py index 1f9786b..ed49453 100644 --- a/ifgen/struct/__init__.py +++ b/ifgen/struct/__init__.py @@ -189,14 +189,15 @@ def create_struct(task: GenerateTask) -> None: with writer.trailing_comment_lines( style=CommentStyle.C_DOXYGEN ) as lines: - lines.append( - ( - "static constexpr " - f"{task.env.config.data['struct_id_underlying']} " - f"id = {task.protocol().id};", - f"{task.name}'s identifier.", + if task.instance["identifier"]: + lines.append( + ( + "static constexpr " + f"{task.env.config.data['struct_id_underlying']} " + f"id = {task.protocol().id};", + f"{task.name}'s identifier.", + ) ) - ) size = task.env.size(task.name) enforce_expected_size(size, task.instance, task.name) diff --git a/ifgen/svd/group/fields.py b/ifgen/svd/group/fields.py index 0badeb6..234f59d 100644 --- a/ifgen/svd/group/fields.py +++ b/ifgen/svd/group/fields.py @@ -17,6 +17,7 @@ "codec": False, "methods": False, "unit_test": False, + "identifier": False, } @@ -148,6 +149,7 @@ def translate_enums(enum: EnumeratedValues) -> dict[str, Any]: "unit_test": False, "json": False, "use_map": False, + "identifier": False, }