Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions aas_core_codegen/cpp/lib/_generate_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,38 @@ def _generate_string_helper_definitions(
)


#: Define the access to an item of a list or a JSON array as Python does it
_AT_DEFINITION: Final[Stripped] = Stripped(
f"""\
/**
* \\brief Access the item of \\p container at \\p index as Python does.
*
* A negative \\p index counts from the back of the \\p container.
*
* We return the reference given by `at`, so that the item can be both read and
* written.
*
* \\param container to be indexed, *e.g.*, a list or a JSON array
* \\param index of the item; negative counts from the back
* \\return reference to the item
* \\throw std::out_of_range if \\p index is out of range
*/
template<typename ContainerT>
auto At(
{I}ContainerT&& container,
{I}int64_t index
) -> decltype(container.at(std::size_t())) {{
{I}if (index < 0) {{
{II}index += static_cast<int64_t>(container.size());
{I}}}

{I}// NOTE (mristin):
{I}// An index which is still negative wraps around to a huge size, so that
{I}// `at` throws.
{I}return container.at(static_cast<std::size_t>(index));
}}"""
)

# fmt: off
@ensure(
lambda result:
Expand Down Expand Up @@ -1420,6 +1452,7 @@ def generate_header(
# and the transpiled code converts the result of ``LenStr`` to ``int64_t``.
# The helpers ``FloorMod`` and ``ParseSafeInt`` return ``int64_t``'s as well.
uses_string_helpers = _uses_string_helpers(type_inference)
uses_list_indexing = intermediate_uses.list_indexing(type_inference)

extra_std_includes: List[str] = []
if (
Expand All @@ -1428,6 +1461,7 @@ def generate_header(
or intermediate_uses.string_find_call(type_inference)
or intermediate_uses.modulo(symbol_table)
or intermediate_uses.safe_integer_checks(symbol_table)
or uses_list_indexing
):
extra_std_includes.append("#include <cstdint>")

Expand Down Expand Up @@ -1671,6 +1705,7 @@ def generate_header(
{I}) != container_end;
}}"""
),
*([_AT_DEFINITION] if uses_list_indexing else []),
Stripped(
"""\
/**
Expand Down
27 changes: 11 additions & 16 deletions aas_core_codegen/cpp/transpilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,36 +544,31 @@ def _declaration(self, definition: parse_tree.Node) -> cpp_aliasing.Declaration:
def _transform_item_access(
self, collection: Stripped, index_node: parse_tree.Expression
) -> Tuple[Optional[Stripped], Optional[Error]]:
"""Access the item of the list ``collection`` at ``index_node``."""
"""
Access the item of the list or JSON array ``collection`` at ``index_node``.
We always go through ``common::At``, which resolves a negative index from
the back as Python does, so that we need not distinguish the literal indices
from the indices known only at run time.
"""
index, error = self._transform_and_value_if_necessary(index_node)
if error is not None:
return None, error
assert index is not None

index_as_int: Optional[int] = None
try:
index_as_int = int(index)
except ValueError:
pass

if index_as_int is not None and index_as_int == -1:
return Stripped(f"{collection}.back()"), None

if index_as_int is not None and index_as_int < -1:
index = Stripped(f"{collection}.size() - {-index_as_int}")

if "\n" in index:
if "\n" in index or "\n" in collection:
return (
Stripped(
f"""\
{collection}.at(
common::At(
{I}{indent_but_first_line(collection, I)},
{I}{indent_but_first_line(index, I)}
)"""
),
None,
)

return Stripped(f"{collection}.at({index})"), None
return Stripped(f"common::At({collection}, {index})"), None

@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
def _transform_mutable_path(
Expand Down
120 changes: 120 additions & 0 deletions aas_core_codegen/csharp/lib/_generate_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,119 @@
)


def _generate_list_helpers(with_json_arrays: bool) -> Stripped:
"""
Generate the access to the items of lists which follows the Python implementation.

If ``with_json_arrays`` is set, we also generate the access to the items of
JSON arrays.
"""
# NOTE (mristin):
# We read from an ``IReadOnlyList``, since both the lists and the covariant
# generic sequences implement it, while ``Nodes.JsonArray`` implements only
# ``IList``. An overload for ``IList`` would be ambiguous for a ``List``, which
# implements both. We write only into the lists, so ``SetAt`` takes
# an ``IList``.
methods: List[Stripped] = [
Stripped(
f"""\
/// <summary>
/// Resolve <paramref name="index" /> in a list of <paramref name="count" /> items.
/// </summary>
/// <exception cref="System.OverflowException">
/// Thrown if the resolved index does not fit into an <c>int</c>.
/// </exception>
private static int ResolveIndex(long index, int count)
{{
{I}return checked((int)(index < 0 ? index + count : index));
}}"""
),
Stripped(
f"""\
/// <summary>
/// Get the item of <paramref name="list" /> at <paramref name="index" />.
/// </summary>
/// <param name="list">to get the item from</param>
/// <param name="index">of the item; negative counts from the back</param>
/// <returns>the item</returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if <paramref name="index" /> is out of range.
/// </exception>
public static T At<T>(
{I}System.Collections.Generic.IReadOnlyList<T> list,
{I}long index
)
{{
{I}return list[ResolveIndex(index, list.Count)];
}}"""
),
]

if with_json_arrays:
methods.append(
Stripped(
f"""\
/// <summary>
/// Get the item of <paramref name="array" /> at <paramref name="index" />.
/// </summary>
/// <param name="array">to get the item from</param>
/// <param name="index">of the item; negative counts from the back</param>
/// <returns>the item</returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if <paramref name="index" /> is out of range.
/// </exception>
public static System.Text.Json.Nodes.JsonNode? At(
{I}System.Text.Json.Nodes.JsonArray array,
{I}long index
)
{{
{I}return array[ResolveIndex(index, array.Count)];
}}"""
)
)

methods.append(
Stripped(
f"""\
/// <summary>
/// Set the item of <paramref name="list" /> at <paramref name="index" />
/// to <paramref name="value" />.
/// </summary>
/// <param name="list">to set the item in</param>
/// <param name="index">of the item; negative counts from the back</param>
/// <param name="value">to be set</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if <paramref name="index" /> is out of range.
/// </exception>
public static void SetAt<T>(
{I}System.Collections.Generic.IList<T> list,
{I}long index,
{I}T value
)
{{
{I}list[ResolveIndex(index, list.Count)] = value;
}}"""
)
)

methods_joined = "\n\n".join(methods)

return Stripped(
f"""\
/// <summary>
/// Provide the access to the items of lists which follows the Python
/// implementation, since Python is the language of the meta-model specifications.
/// </summary>
/// <remarks>
/// A negative index counts from the back of the list.
/// </remarks>
public static class ListHelpers
{{
{I}{indent_but_first_line(methods_joined, I)}
}} // public static class ListHelpers"""
)


def _generate_string_helpers(
symbol_table: intermediate.SymbolTable,
type_inference: intermediate_type_inference.TypeInference,
Expand Down Expand Up @@ -809,6 +922,13 @@ def generate(
):
blocks.append(_generate_string_helpers(symbol_table, type_inference))

if intermediate_uses.list_indexing(type_inference):
blocks.append(
_generate_list_helpers(
with_json_arrays=intermediate_uses.json_array_indexing(type_inference)
)
)

if intermediate_uses.set_properties(symbol_table):
blocks.append(_generate_set_helpers(symbol_table))

Expand Down
Loading
Loading