summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2021-06-16 19:06:38 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2021-06-16 19:08:46 +0200
commitcdb62c9f8cd591297bff1c3384dc56d5c752a7bd (patch)
treeec4512df23ea8f4b0414d71a43f7e179ea222e54
parentinterface: Fix mapper prefix (diff)
downloadrtems-central-cdb62c9f8cd591297bff1c3384dc56d5c752a7bd.tar.bz2
validation: Split file
-rw-r--r--rtemsspec/transitionmap.py670
-rw-r--r--rtemsspec/validation.py645
-rwxr-xr-xspecview.py2
3 files changed, 676 insertions, 641 deletions
diff --git a/rtemsspec/transitionmap.py b/rtemsspec/transitionmap.py
new file mode 100644
index 00000000..c7f2193a
--- /dev/null
+++ b/rtemsspec/transitionmap.py
@@ -0,0 +1,670 @@
+# SPDX-License-Identifier: BSD-2-Clause
+"""
+This module provides the Transition and TransitionMap classes used to work with
+action requirements.
+"""
+
+# Copyright (C) 2020, 2021 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import itertools
+import math
+import textwrap
+from typing import Any, Dict, Iterator, List, NamedTuple, Optional, Tuple
+
+from rtemsspec.content import CContent, enabled_by_to_exp, ExpressionMapper
+from rtemsspec.items import is_enabled, Item
+
+
+class Transition(NamedTuple):
+ """ Represents a action requirement transition map entry. """
+ desc_idx: int
+ enabled_by: Any
+ skip: int
+ pre_cond_na: Tuple[int, ...]
+ post_cond: Tuple[Any, ...]
+
+
+def _variant_to_key(variant: Transition) -> str:
+ return "".join((enabled_by_to_exp(variant.enabled_by,
+ ExpressionMapper()), str(variant.skip),
+ str(variant.pre_cond_na), str(variant.post_cond)))
+
+
+class _TransitionEntry:
+ def __init__(self):
+ self.key = ""
+ self.variants = [] # type: List[Transition]
+
+ def __bool__(self):
+ return bool(self.variants)
+
+ def __getitem__(self, key):
+ return self.variants[key]
+
+ def __len__(self):
+ return len(self.variants)
+
+ def add(self, variant: Transition) -> None:
+ """ Adds the variant to the transitions of the entry. """
+ self.key += _variant_to_key(variant)
+ self.variants.append(variant)
+
+ def replace(self, index: int, variant: Transition) -> None:
+ """ Replace the variant at transition variant index. """
+ self.key = self.key.replace(_variant_to_key(self.variants[index]),
+ _variant_to_key(variant))
+ self.variants[index] = variant
+
+
+_TransitionMap = List[_TransitionEntry]
+
+
+def _to_st_idx(conditions: List[Any]) -> Tuple[Dict[str, int], ...]:
+ return tuple(
+ dict((state["name"], st_idx) for st_idx, state in enumerate(
+ itertools.chain(condition["states"], [{
+ "name": "N/A"
+ }]))) for condition in conditions)
+
+
+def _to_st_name(conditions: List[Any]) -> Tuple[Tuple[str, ...], ...]:
+ return tuple(
+ tuple(
+ itertools.chain((state["name"]
+ for state in condition["states"]), ["NA"]))
+ for condition in conditions)
+
+
+class _PostCondContext(NamedTuple):
+ transition_map: "TransitionMap"
+ map_idx: int
+ pre_co_states: Tuple[int, ...]
+ post_co_states: Tuple[Any, ...]
+ post_co_idx: int
+ ops: Any
+
+
+def _post_cond_bool_and(ctx: _PostCondContext, exp: Any) -> bool:
+ for element in exp:
+ if not _post_cond_bool_exp(ctx, element):
+ return False
+ return True
+
+
+def _post_cond_bool_not(ctx: _PostCondContext, exp: Any) -> bool:
+ return not _post_cond_bool_exp(ctx, exp)
+
+
+def _post_cond_bool_or(ctx: _PostCondContext, exp: Any) -> bool:
+ for element in exp:
+ if _post_cond_bool_exp(ctx, element):
+ return True
+ return False
+
+
+def _post_cond_bool_post_cond(ctx: _PostCondContext, exp: Any) -> bool:
+ for post_co_name, status in exp.items():
+ if isinstance(status, str):
+ status = [status]
+ post_co_idx = ctx.transition_map.post_co_name_to_co_idx(post_co_name)
+ st_idx = [
+ ctx.transition_map.post_co_idx_st_name_to_st_idx(
+ post_co_idx, st_name) for st_name in status
+ ]
+ if ctx.post_co_states[post_co_idx] not in st_idx:
+ return False
+ return True
+
+
+def _post_cond_bool_pre_cond(ctx: _PostCondContext, exp: Any) -> bool:
+ for pre_co_name, status in exp.items():
+ if isinstance(status, str):
+ status = [status]
+ pre_co_idx = ctx.transition_map.pre_co_name_to_co_idx(pre_co_name)
+ st_idx = [
+ ctx.transition_map.pre_co_idx_st_name_to_st_idx(
+ pre_co_idx, st_name) for st_name in status
+ ]
+ if ctx.pre_co_states[pre_co_idx] not in st_idx:
+ return False
+ return True
+
+
+_POST_COND_BOOL_OPS = {
+ "and": _post_cond_bool_and,
+ "not": _post_cond_bool_not,
+ "or": _post_cond_bool_or,
+ "post-conditions": _post_cond_bool_post_cond,
+ "pre-conditions": _post_cond_bool_pre_cond,
+}
+
+
+def _post_cond_bool_exp(ctx: _PostCondContext, exp: Any) -> Optional[int]:
+ if isinstance(exp, list):
+ return _post_cond_bool_or(ctx, exp)
+ key = next(iter(exp))
+ return _POST_COND_BOOL_OPS[key](ctx, exp[key])
+
+
+def _post_cond_do_specified_by(ctx: _PostCondContext, pre_co_name: str) -> int:
+ pre_co_idx = ctx.transition_map.pre_co_name_to_co_idx(pre_co_name)
+ st_name = ctx.transition_map.pre_co_idx_st_idx_to_st_name(
+ pre_co_idx, ctx.pre_co_states[pre_co_idx])
+ return ctx.transition_map.post_co_idx_st_name_to_st_idx(
+ ctx.post_co_idx, st_name)
+
+
+def _post_cond_if(ctx: _PostCondContext) -> Optional[int]:
+ if _post_cond_bool_exp(ctx, ctx.ops["if"]):
+ if "then-specified-by" in ctx.ops:
+ return _post_cond_do_specified_by(ctx,
+ ctx.ops["then-specified-by"])
+ return ctx.transition_map.post_co_idx_st_name_to_st_idx(
+ ctx.post_co_idx, ctx.ops["then"])
+ return None
+
+
+def _post_cond_specified_by(ctx: _PostCondContext) -> Optional[int]:
+ return _post_cond_do_specified_by(ctx, ctx.ops["specified-by"])
+
+
+def _post_cond_else(ctx: _PostCondContext) -> Optional[int]:
+ return ctx.transition_map.post_co_idx_st_name_to_st_idx(
+ ctx.post_co_idx, ctx.ops["else"])
+
+
+_POST_COND_OP = {
+ "else": _post_cond_else,
+ "if": _post_cond_if,
+ "specified-by": _post_cond_specified_by,
+}
+
+PostCond = Tuple[int, ...]
+
+PreCondsOfPostCond = List[Tuple[List[int], ...]]
+
+
+def _compact(pre_conds: PreCondsOfPostCond) -> PreCondsOfPostCond:
+ while True:
+ last = pre_conds[0]
+ combined_pre_conds = [last]
+ combined_count = 0
+ for row in pre_conds[1:]:
+ diff = [
+ index for index, states in enumerate(last)
+ if states != row[index]
+ ]
+ if len(diff) == 1:
+ index = diff[0]
+ combined_count += 1
+ last[index].extend(row[index])
+ else:
+ combined_pre_conds.append(row)
+ last = row
+ pre_conds = combined_pre_conds
+ if combined_count == 0:
+ break
+ return pre_conds
+
+
+def _compact_more(pre_conds: PreCondsOfPostCond) -> PreCondsOfPostCond:
+ while True:
+ combined_count = 0
+ next_pre_conds = []
+ while pre_conds:
+ first = pre_conds.pop(0)
+ next_pre_conds.append(first)
+ for row in pre_conds:
+ diff = [
+ index for index, states in enumerate(first)
+ if states != row[index]
+ ]
+ if len(diff) <= 1:
+ if diff:
+ index = diff[0]
+ first[index].extend(row[index])
+ combined_count += 1
+ pre_conds.remove(row)
+ pre_conds = next_pre_conds
+ if combined_count == 0:
+ break
+ return pre_conds
+
+
+class TransitionMap:
+ """ Representation of an action requirement transition map. """
+
+ # pylint: disable=too-many-instance-attributes
+ def __init__(self, item: Item):
+ self._item = item
+ self._pre_co_count = len(item["pre-conditions"])
+ self._post_co_count = len(item["post-conditions"])
+ self.pre_co_summary = tuple(0 for _ in range(self._pre_co_count + 1))
+ self._pre_co_idx_st_idx_to_st_name = _to_st_name(
+ item["pre-conditions"])
+ self._post_co_idx_st_idx_to_st_name = _to_st_name(
+ item["post-conditions"])
+ self._pre_co_idx_st_name_to_st_idx = _to_st_idx(item["pre-conditions"])
+ self._post_co_idx_st_name_to_st_idx = _to_st_idx(
+ item["post-conditions"])
+ self._pre_co_idx_to_cond = dict(
+ (co_idx, condition)
+ for co_idx, condition in enumerate(item["pre-conditions"]))
+ self._pre_co_name_to_co_idx = dict(
+ (condition["name"], co_idx)
+ for co_idx, condition in enumerate(item["pre-conditions"]))
+ self._post_co_name_to_co_idx = dict(
+ (condition["name"], co_idx)
+ for co_idx, condition in enumerate(item["post-conditions"]))
+ self._post_co_idx_to_co_name = dict(
+ (co_idx, condition["name"])
+ for co_idx, condition in enumerate(item["post-conditions"]))
+ self._skip_idx_to_name = dict(
+ (skip_idx + 1, key)
+ for skip_idx, key in enumerate(item["skip-reasons"].keys()))
+ self._skip_name_to_idx = dict(
+ (key, skip_idx + 1)
+ for skip_idx, key in enumerate(item["skip-reasons"].keys()))
+ self._entries = {} # type: Dict[str, List[Any]]
+ self._map = self._build_map()
+ self._post_process()
+
+ def __getitem__(self, key: str):
+ return self._item[key]
+
+ def __iter__(self):
+ yield from self._map
+
+ def entries(self) -> Iterator[List[Any]]:
+ """ Yields the transition map entry variants sorted by frequency. """
+ yield from sorted(self._entries.values(), key=lambda x: x[1])
+
+ def get_variants(self,
+ enabled: List[str]) -> Iterator[Tuple[int, Transition]]:
+ """
+ Yields the map index and the transition variants enabled by the enabled
+ list.
+ """
+ for map_idx, transitions in enumerate(self._map):
+ for variant in transitions[1:]:
+ if is_enabled(enabled, variant.enabled_by):
+ break
+ else:
+ variant = transitions[0]
+ yield map_idx, variant
+
+ def get_post_conditions(
+ self, enabled: List[str]
+ ) -> Iterator[Tuple[PostCond, PreCondsOfPostCond]]:
+ """
+ Yields tuples of post-condition variants and the corresponding
+ pre-condition variants which are enabled by the enabled list.
+
+ The first entry of the post-condition variant is the skip index. The
+ remaining entries are post-condition indices. The pre-condition
+ variants are a list of tuples. Each tuple entry corresponds to a
+ pre-condition and provides a list of corresponding pre-condition state
+ indices.
+ """
+ entries = {} # type: Dict[PostCond, PreCondsOfPostCond]
+ for map_idx, variant in self.get_variants(enabled):
+ key = (variant.skip, ) + variant.post_cond
+ entry = entries.setdefault(key, [])
+ entry.append(
+ tuple([state] for state in self.map_idx_to_pre_co_states(
+ map_idx, variant.pre_cond_na)))
+ for post_cond, pre_conds in sorted(entries.items(),
+ key=lambda x: (x[0][0], len(x[1]))):
+ pre_conds = _compact_more(_compact(pre_conds))
+ yield post_cond, pre_conds
+
+ def _post_process(self) -> None:
+ for map_idx, transitions in enumerate(self):
+ if not transitions or not isinstance(
+ transitions[0].enabled_by,
+ bool) or not transitions[0].enabled_by:
+ raise ValueError(
+ f"transition map of {self._item.spec} contains no default "
+ "entry for pre-condition set "
+ f"{{{self._map_index_to_pre_conditions(map_idx)}}}")
+ entry = self._entries.setdefault(transitions.key,
+ [0, 0, transitions, []])
+ entry[0] += 1
+ entry[3].append(map_idx)
+ for index, entry in enumerate(
+ sorted(self._entries.values(),
+ key=lambda x: x[0],
+ reverse=True)):
+ entry[1] = index
+
+ def _map_index_to_pre_conditions(self, map_idx: int) -> str:
+ conditions = []
+ for condition in reversed(self._item["pre-conditions"]):
+ states = condition["states"]
+ count = len(states)
+ st_idx = int(map_idx % count)
+ conditions.append(f"{condition['name']}={states[st_idx]['name']}")
+ map_idx //= count
+ return ", ".join(reversed(conditions))
+
+ def map_idx_to_pre_co_states(
+ self, map_idx: int, pre_cond_na: Tuple[int,
+ ...]) -> Tuple[int, ...]:
+ """
+ Maps the transition map index and the associated pre-condition state
+ indices.
+ """
+ co_states = []
+ for condition in reversed(self._item["pre-conditions"]):
+ count = len(condition["states"])
+ co_states.append(count if pre_cond_na[self._pre_co_name_to_co_idx[
+ condition["name"]]] else int(map_idx % count))
+ map_idx //= count
+ return tuple(reversed(co_states))
+
+ def pre_co_name_to_co_idx(self, co_name: str) -> int:
+ """
+ Maps the pre-condition name to the associated pre-condition index.
+ """
+ return self._pre_co_name_to_co_idx[co_name]
+
+ def pre_co_idx_to_co_name(self, co_idx: int) -> str:
+ """
+ Maps the pre-condition index to the associated pre-condition name.
+ """
+ return self._pre_co_idx_to_cond[co_idx]["name"]
+
+ def post_co_name_to_co_idx(self, co_name: str) -> int:
+ """
+ Maps the post-condition name to the associated post-condition index.
+ """
+ return self._post_co_name_to_co_idx[co_name]
+
+ def post_co_idx_to_co_name(self, co_idx: int) -> str:
+ """
+ Maps the post-condition index to the associated post-condition name.
+ """
+ return self._post_co_idx_to_co_name[co_idx]
+
+ def pre_co_idx_st_idx_to_st_name(self, co_idx: int, st_idx: int) -> str:
+ """
+ Maps the pre-condition name and state index to the associated state
+ name.
+ """
+ return self._pre_co_idx_st_idx_to_st_name[co_idx][st_idx]
+
+ def post_co_idx_st_idx_to_st_name(self, co_idx: int, st_idx: int) -> str:
+ """
+ Maps the post-condition name and state index to the associated state
+ name.
+ """
+ return self._post_co_idx_st_idx_to_st_name[co_idx][st_idx]
+
+ def pre_co_idx_st_name_to_st_idx(self, co_idx: int, st_name: str) -> int:
+ """
+ Maps the pre-condition index and state name to the associated state
+ index.
+ """
+ return self._pre_co_idx_st_name_to_st_idx[co_idx][st_name]
+
+ def post_co_idx_st_name_to_st_idx(self, co_idx: int, st_name: str) -> int:
+ """
+ Maps the post-condition index and state name to the associated state
+ index.
+ """
+ return self._post_co_idx_st_name_to_st_idx[co_idx][st_name]
+
+ def skip_idx_to_name(self, skip_idx: int) -> str:
+ """
+ Maps the skip index the associated skip name index.
+ """
+ return self._skip_idx_to_name[skip_idx]
+
+ def _map_post_cond(self, map_idx: int, co_idx: int,
+ variant: Transition) -> Transition:
+ if isinstance(variant.post_cond[co_idx], int):
+ return variant
+ pre_co_states = self.map_idx_to_pre_co_states(map_idx,
+ variant.pre_cond_na)
+ for ops in variant.post_cond[co_idx]:
+ idx = _POST_COND_OP[next(iter(ops))](_PostCondContext(
+ self, map_idx, pre_co_states, variant.post_cond, co_idx, ops))
+ if idx is not None:
+ return Transition(
+ variant.desc_idx, variant.enabled_by, variant.skip,
+ variant.pre_cond_na, variant.post_cond[0:co_idx] +
+ (idx, ) + variant.post_cond[co_idx + 1:])
+ raise ValueError(
+ "cannot determine state for post-condition "
+ f"'{self._post_co_idx_to_co_name[co_idx]}' of transition map "
+ f"descriptor {variant.desc_idx} of {self._item.spec} for "
+ "pre-condition set "
+ f"{{{self._map_index_to_pre_conditions(map_idx)}}}")
+
+ def _make_post_cond(self, map_idx: int, variant: Transition) -> Transition:
+ for co_idx in range(len(variant.post_cond)):
+ variant = self._map_post_cond(map_idx, co_idx, variant)
+ return variant
+
+ def _add_variant(self, transition_map: _TransitionMap, map_idx: int,
+ variant: Transition) -> None:
+ if transition_map[map_idx]:
+ for index, existing in enumerate(transition_map[map_idx].variants):
+ if existing.enabled_by == variant.enabled_by:
+ if variant.skip:
+ # Allow transition map variants with a skip reason to
+ # overwrite existing variants with the same enabled-by
+ # attribute. This is important if variants use N/A for
+ # some pre-conditions. It makes it also easier to skip
+ # pre-conditon states which are controlled by build
+ # options.
+ transition_map[map_idx].replace(index, variant)
+ return
+ raise ValueError(
+ f"transition map descriptor {variant.desc_idx} of "
+ f"{self._item.spec} duplicates pre-condition set "
+ f"{{{self._map_index_to_pre_conditions(map_idx)}}}"
+ " defined by transition map descriptor "
+ f"{existing.desc_idx}")
+ default = transition_map[map_idx][0]
+ if (default.post_cond, default.skip,
+ default.pre_cond_na) == (variant.post_cond, variant.skip,
+ variant.pre_cond_na):
+ return
+ elif not isinstance(variant.enabled_by,
+ bool) or not variant.enabled_by:
+ raise ValueError(
+ f"transition map descriptor {variant.desc_idx} of "
+ f"{self._item.spec} is the first variant for "
+ f"{{{self._map_index_to_pre_conditions(map_idx)}}} "
+ "and it is not enabled by default")
+ self.pre_co_summary = tuple(
+ a + b for a, b in zip(self.pre_co_summary, (variant.skip, ) +
+ variant.pre_cond_na))
+ transition_map[map_idx].add(variant)
+
+ def _add_transitions(self, transition_map: _TransitionMap,
+ desc: Dict[str, Any], desc_idx: int,
+ skip_post_cond: Tuple[Any, ...], co_idx: int,
+ map_idx: int, pre_cond_na: Tuple[int, ...]) -> None:
+ # pylint: disable=too-many-arguments
+ # pylint: disable=too-many-locals
+ if co_idx < self._pre_co_count:
+ condition = self._pre_co_idx_to_cond[co_idx]
+ state_count = len(condition["states"])
+ map_idx *= state_count
+ states = desc["pre-conditions"][condition["name"]]
+ if isinstance(states, str):
+ assert states in ["all", "N/A"]
+ for st_idx in range(state_count):
+ self._add_transitions(
+ transition_map, desc, desc_idx, skip_post_cond,
+ co_idx + 1, map_idx + st_idx,
+ pre_cond_na + (int(states == "N/A"), ))
+ else:
+ for st_name in states:
+ try:
+ st_idx = self._pre_co_idx_st_name_to_st_idx[co_idx][
+ st_name]
+ except KeyError as err:
+ msg = (f"transition map descriptor {desc_idx} of "
+ f"{self._item.spec} refers to non-existent "
+ f"state {err} of pre-condition "
+ f"'{condition['name']}'")
+ raise ValueError(msg) from err
+ self._add_transitions(transition_map, desc, desc_idx,
+ skip_post_cond, co_idx + 1,
+ map_idx + st_idx,
+ pre_cond_na + (0, ))
+ else:
+ variant = self._make_post_cond(
+ map_idx,
+ Transition(desc_idx, desc["enabled-by"], skip_post_cond[0],
+ pre_cond_na, skip_post_cond[1:]))
+ self._add_variant(transition_map, map_idx, variant)
+
+ def _add_default(self, transition_map: _TransitionMap, desc: Dict[str,
+ Any],
+ desc_idx: int, skip_post_cond: Tuple[int, ...]) -> None:
+ enabled_by = desc["enabled-by"]
+ for map_idx, transition in enumerate(transition_map):
+ if not transition:
+ transition.add(
+ self._make_post_cond(
+ map_idx,
+ Transition(desc_idx, enabled_by, skip_post_cond[0],
+ (0, ) * self._pre_co_count,
+ skip_post_cond[1:])))
+
+ def _get_post_cond(self, desc: Dict[str, Any], co_idx: int) -> Any:
+ info = desc["post-conditions"][self._post_co_idx_to_co_name[co_idx]]
+ if isinstance(info, str):
+ return self._post_co_idx_st_name_to_st_idx[co_idx][info]
+ return info
+
+ def _build_map(self) -> _TransitionMap:
+ transition_count = 1
+ for condition in self["pre-conditions"]:
+ state_count = len(condition["states"])
+ if state_count == 0:
+ raise ValueError(f"pre-condition '{condition['name']}' of "
+ f"{self._item.spec} has no states")
+ transition_count *= state_count
+ transition_map = [_TransitionEntry() for _ in range(transition_count)]
+ for desc_idx, desc in enumerate(self["transition-map"]):
+ if isinstance(desc["post-conditions"], dict):
+ try:
+ skip_post_cond = (0, ) + tuple(
+ self._get_post_cond(desc, co_idx)
+ for co_idx in range(self._post_co_count))
+ except KeyError as err:
+ msg = (f"transition map descriptor {desc_idx} of "
+ f"{self._item.spec} refers to non-existent "
+ f"post-condition state {err}")
+ raise ValueError(msg) from err
+ else:
+ skip_post_cond = (
+ self._skip_name_to_idx[desc["post-conditions"]], ) + tuple(
+ self._post_co_idx_st_name_to_st_idx[co_idx]["N/A"]
+ for co_idx in range(self._post_co_count))
+ if isinstance(desc["pre-conditions"], dict):
+ self._add_transitions(transition_map, desc, desc_idx,
+ skip_post_cond, 0, 0, ())
+ else:
+ assert desc["pre-conditions"] == "default"
+ self._add_default(transition_map, desc, desc_idx,
+ skip_post_cond)
+ return transition_map
+
+ def _get_entry(self, ident: str, variant: Transition) -> str:
+ text = "{ " + ", ".join(
+ itertools.chain(
+ map(str, (int(variant.skip != 0), ) + variant.pre_cond_na),
+ ((f"{ident}_Post_{self._post_co_idx_to_co_name[co_idx]}"
+ f"_{self._post_co_idx_st_idx_to_st_name[co_idx][st_idx]}")
+ for co_idx, st_idx in enumerate(variant.post_cond))))
+ wrapper = textwrap.TextWrapper()
+ wrapper.initial_indent = " "
+ wrapper.subsequent_indent = " "
+ wrapper.width = 79
+ return "\n".join(wrapper.wrap(text)) + " },"
+
+ def _get_entry_bits(self) -> int:
+ bits = self._pre_co_count + 1
+ for st_idx_to_st_name in self._post_co_idx_st_idx_to_st_name:
+ bits += math.ceil(math.log2(len(st_idx_to_st_name)))
+ return 2**max(math.ceil(math.log2(bits)), 3)
+
+ def add_map(self, content: CContent, ident: str) -> None:
+ """ Adds the transition map definitions to the content. """
+ entries = []
+ mapper = ExpressionMapper()
+ for entry in self.entries():
+ transitions = entry[2]
+ if len(transitions) == 1:
+ entries.append(self._get_entry(ident, transitions[0]))
+ else:
+ ifelse = "#if "
+ enumerators = [] # type: List[str]
+ for variant in transitions[1:]:
+ enumerators.append(
+ ifelse + enabled_by_to_exp(variant.enabled_by, mapper))
+ enumerators.append(self._get_entry(ident, variant))
+ ifelse = "#elif "
+ enumerators.append("#else")
+ enumerators.append(self._get_entry(ident, transitions[0]))
+ enumerators.append("#endif")
+ entries.append("\n".join(enumerators))
+ bits = self._get_entry_bits()
+ content.add("typedef struct {")
+ with content.indent():
+ content.append(f"uint{bits}_t Skip : 1;")
+ for condition in self["pre-conditions"]:
+ content.append(f"uint{bits}_t Pre_{condition['name']}_NA : 1;")
+ for condition in self["post-conditions"]:
+ state_bits = math.ceil(math.log2(len(condition["states"]) + 1))
+ content.append(
+ f"uint{bits}_t Post_{condition['name']} : {state_bits};")
+ content.add(f"}} {ident}_Entry;")
+ content.add([f"static const {ident}_Entry", f"{ident}_Entries[] = {{"])
+ entries[-1] = entries[-1].replace("},", "}")
+ content.append(entries)
+ bits = max(8, math.ceil(math.log2(len(self._entries)) / 8) * 8)
+ content.append(
+ ["};", "", f"static const uint{bits}_t", f"{ident}_Map[] = {{"])
+ text = ", ".join(
+ str(self._entries[transitions.key][1])
+ for transitions in self._map)
+ wrapper = textwrap.TextWrapper()
+ wrapper.initial_indent = " "
+ wrapper.subsequent_indent = " "
+ wrapper.width = 79
+ content.append(wrapper.wrap(text))
+ content.append("};")
+
+ def get_post_entry_member(self, co_idx: int) -> str:
+ """
+ Gets the post-condition entry member name for the post-condition index.
+ """
+ return f"Post_{self._post_co_idx_to_co_name[co_idx]}"
diff --git a/rtemsspec/validation.py b/rtemsspec/validation.py
index 70ed7380..24efc210 100644
--- a/rtemsspec/validation.py
+++ b/rtemsspec/validation.py
@@ -24,20 +24,17 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-# pylint: disable=too-many-lines
-
import itertools
-import math
import os
import re
-import textwrap
-from typing import Any, Dict, Iterator, List, NamedTuple, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple
-from rtemsspec.content import CContent, CInclude, enabled_by_to_exp, \
- ExpressionMapper, GenericContent, get_value_params, get_value_plural, \
+from rtemsspec.content import CContent, CInclude, \
+ GenericContent, get_value_params, get_value_plural, \
get_value_doxygen_group, get_value_doxygen_function, to_camel_case
-from rtemsspec.items import is_enabled, Item, ItemCache, \
+from rtemsspec.items import Item, ItemCache, \
ItemGetValueContext, ItemMapper
+from rtemsspec.transitionmap import TransitionMap
ItemMap = Dict[str, Item]
@@ -466,639 +463,7 @@ class _TestSuiteItem(_TestItem):
content.add("/** @} */")
-class Transition(NamedTuple):
- """ Represents a action requirement transition map entry. """
- desc_idx: int
- enabled_by: Any
- skip: int
- pre_cond_na: Tuple[int, ...]
- post_cond: Tuple[Any, ...]
-
-
-def _variant_to_key(variant: Transition) -> str:
- return "".join((enabled_by_to_exp(variant.enabled_by,
- ExpressionMapper()), str(variant.skip),
- str(variant.pre_cond_na), str(variant.post_cond)))
-
-
-class _TransitionEntry:
- def __init__(self):
- self.key = ""
- self.variants = [] # type: List[Transition]
-
- def __bool__(self):
- return bool(self.variants)
-
- def __getitem__(self, key):
- return self.variants[key]
-
- def __len__(self):
- return len(self.variants)
-
- def add(self, variant: Transition) -> None:
- """ Adds the variant to the transitions of the entry. """
- self.key += _variant_to_key(variant)
- self.variants.append(variant)
-
- def replace(self, index: int, variant: Transition) -> None:
- """ Replace the variant at transition variant index. """
- self.key = self.key.replace(_variant_to_key(self.variants[index]),
- _variant_to_key(variant))
- self.variants[index] = variant
-
-
_IdxToX = Tuple[Tuple[str, ...], ...]
-_TransitionMap = List[_TransitionEntry]
-
-
-def _to_st_idx(conditions: List[Any]) -> Tuple[Dict[str, int], ...]:
- return tuple(
- dict((state["name"], st_idx) for st_idx, state in enumerate(
- itertools.chain(condition["states"], [{
- "name": "N/A"
- }]))) for condition in conditions)
-
-
-def _to_st_name(conditions: List[Any]) -> _IdxToX:
- return tuple(
- tuple(
- itertools.chain((state["name"]
- for state in condition["states"]), ["NA"]))
- for condition in conditions)
-
-
-class _PostCondContext(NamedTuple):
- transition_map: "TransitionMap"
- map_idx: int
- pre_co_states: Tuple[int, ...]
- post_co_states: Tuple[Any, ...]
- post_co_idx: int
- ops: Any
-
-
-def _post_cond_bool_and(ctx: _PostCondContext, exp: Any) -> bool:
- for element in exp:
- if not _post_cond_bool_exp(ctx, element):
- return False
- return True
-
-
-def _post_cond_bool_not(ctx: _PostCondContext, exp: Any) -> bool:
- return not _post_cond_bool_exp(ctx, exp)
-
-
-def _post_cond_bool_or(ctx: _PostCondContext, exp: Any) -> bool:
- for element in exp:
- if _post_cond_bool_exp(ctx, element):
- return True
- return False
-
-
-def _post_cond_bool_post_cond(ctx: _PostCondContext, exp: Any) -> bool:
- for post_co_name, status in exp.items():
- if isinstance(status, str):
- status = [status]
- post_co_idx = ctx.transition_map.post_co_name_to_co_idx(post_co_name)
- st_idx = [
- ctx.transition_map.post_co_idx_st_name_to_st_idx(
- post_co_idx, st_name) for st_name in status
- ]
- if ctx.post_co_states[post_co_idx] not in st_idx:
- return False
- return True
-
-
-def _post_cond_bool_pre_cond(ctx: _PostCondContext, exp: Any) -> bool:
- for pre_co_name, status in exp.items():
- if isinstance(status, str):
- status = [status]
- pre_co_idx = ctx.transition_map.pre_co_name_to_co_idx(pre_co_name)
- st_idx = [
- ctx.transition_map.pre_co_idx_st_name_to_st_idx(
- pre_co_idx, st_name) for st_name in status
- ]
- if ctx.pre_co_states[pre_co_idx] not in st_idx:
- return False
- return True
-
-
-_POST_COND_BOOL_OPS = {
- "and": _post_cond_bool_and,
- "not": _post_cond_bool_not,
- "or": _post_cond_bool_or,
- "post-conditions": _post_cond_bool_post_cond,
- "pre-conditions": _post_cond_bool_pre_cond,
-}
-
-
-def _post_cond_bool_exp(ctx: _PostCondContext, exp: Any) -> Optional[int]:
- if isinstance(exp, list):
- return _post_cond_bool_or(ctx, exp)
- key = next(iter(exp))
- return _POST_COND_BOOL_OPS[key](ctx, exp[key])
-
-
-def _post_cond_do_specified_by(ctx: _PostCondContext, pre_co_name: str) -> int:
- pre_co_idx = ctx.transition_map.pre_co_name_to_co_idx(pre_co_name)
- st_name = ctx.transition_map.pre_co_idx_st_idx_to_st_name(
- pre_co_idx, ctx.pre_co_states[pre_co_idx])
- return ctx.transition_map.post_co_idx_st_name_to_st_idx(
- ctx.post_co_idx, st_name)
-
-
-def _post_cond_if(ctx: _PostCondContext) -> Optional[int]:
- if _post_cond_bool_exp(ctx, ctx.ops["if"]):
- if "then-specified-by" in ctx.ops:
- return _post_cond_do_specified_by(ctx,
- ctx.ops["then-specified-by"])
- return ctx.transition_map.post_co_idx_st_name_to_st_idx(
- ctx.post_co_idx, ctx.ops["then"])
- return None
-
-
-def _post_cond_specified_by(ctx: _PostCondContext) -> Optional[int]:
- return _post_cond_do_specified_by(ctx, ctx.ops["specified-by"])
-
-
-def _post_cond_else(ctx: _PostCondContext) -> Optional[int]:
- return ctx.transition_map.post_co_idx_st_name_to_st_idx(
- ctx.post_co_idx, ctx.ops["else"])
-
-
-_POST_COND_OP = {
- "else": _post_cond_else,
- "if": _post_cond_if,
- "specified-by": _post_cond_specified_by,
-}
-
-PostCond = Tuple[int, ...]
-
-PreCondsOfPostCond = List[Tuple[List[int], ...]]
-
-
-def _compact(pre_conds: PreCondsOfPostCond) -> PreCondsOfPostCond:
- while True:
- last = pre_conds[0]
- combined_pre_conds = [last]
- combined_count = 0
- for row in pre_conds[1:]:
- diff = [
- index for index, states in enumerate(last)
- if states != row[index]
- ]
- if len(diff) == 1:
- index = diff[0]
- combined_count += 1
- last[index].extend(row[index])
- else:
- combined_pre_conds.append(row)
- last = row
- pre_conds = combined_pre_conds
- if combined_count == 0:
- break
- return pre_conds
-
-
-def _compact_more(pre_conds: PreCondsOfPostCond) -> PreCondsOfPostCond:
- while True:
- combined_count = 0
- next_pre_conds = []
- while pre_conds:
- first = pre_conds.pop(0)
- next_pre_conds.append(first)
- for row in pre_conds:
- diff = [
- index for index, states in enumerate(first)
- if states != row[index]
- ]
- if len(diff) <= 1:
- if diff:
- index = diff[0]
- first[index].extend(row[index])
- combined_count += 1
- pre_conds.remove(row)
- pre_conds = next_pre_conds
- if combined_count == 0:
- break
- return pre_conds
-
-
-class TransitionMap:
- """ Representation of an action requirement transition map. """
-
- # pylint: disable=too-many-instance-attributes
- def __init__(self, item: Item):
- self._item = item
- self._pre_co_count = len(item["pre-conditions"])
- self._post_co_count = len(item["post-conditions"])
- self.pre_co_summary = tuple(0 for _ in range(self._pre_co_count + 1))
- self._pre_co_idx_st_idx_to_st_name = _to_st_name(
- item["pre-conditions"])
- self._post_co_idx_st_idx_to_st_name = _to_st_name(
- item["post-conditions"])
- self._pre_co_idx_st_name_to_st_idx = _to_st_idx(item["pre-conditions"])
- self._post_co_idx_st_name_to_st_idx = _to_st_idx(
- item["post-conditions"])
- self._pre_co_idx_to_cond = dict(
- (co_idx, condition)
- for co_idx, condition in enumerate(item["pre-conditions"]))
- self._pre_co_name_to_co_idx = dict(
- (condition["name"], co_idx)
- for co_idx, condition in enumerate(item["pre-conditions"]))
- self._post_co_name_to_co_idx = dict(
- (condition["name"], co_idx)
- for co_idx, condition in enumerate(item["post-conditions"]))
- self._post_co_idx_to_co_name = dict(
- (co_idx, condition["name"])
- for co_idx, condition in enumerate(item["post-conditions"]))
- self._skip_idx_to_name = dict(
- (skip_idx + 1, key)
- for skip_idx, key in enumerate(item["skip-reasons"].keys()))
- self._skip_name_to_idx = dict(
- (key, skip_idx + 1)
- for skip_idx, key in enumerate(item["skip-reasons"].keys()))
- self._entries = {} # type: Dict[str, List[Any]]
- self._map = self._build_map()
- self._post_process()
-
- def __getitem__(self, key: str):
- return self._item[key]
-
- def __iter__(self):
- yield from self._map
-
- def entries(self) -> Iterator[List[Any]]:
- """ Yields the transition map entry variants sorted by frequency. """
- yield from sorted(self._entries.values(), key=lambda x: x[1])
-
- def get_variants(self,
- enabled: List[str]) -> Iterator[Tuple[int, Transition]]:
- """
- Yields the map index and the transition variants enabled by the enabled
- list.
- """
- for map_idx, transitions in enumerate(self._map):
- for variant in transitions[1:]:
- if is_enabled(enabled, variant.enabled_by):
- break
- else:
- variant = transitions[0]
- yield map_idx, variant
-
- def get_post_conditions(
- self, enabled: List[str]
- ) -> Iterator[Tuple[PostCond, PreCondsOfPostCond]]:
- """
- Yields tuples of post-condition variants and the corresponding
- pre-condition variants which are enabled by the enabled list.
-
- The first entry of the post-condition variant is the skip index. The
- remaining entries are post-condition indices. The pre-condition
- variants are a list of tuples. Each tuple entry corresponds to a
- pre-condition and provides a list of corresponding pre-condition state
- indices.
- """
- entries = {} # type: Dict[PostCond, PreCondsOfPostCond]
- for map_idx, variant in self.get_variants(enabled):
- key = (variant.skip, ) + variant.post_cond
- entry = entries.setdefault(key, [])
- entry.append(
- tuple([state] for state in self.map_idx_to_pre_co_states(
- map_idx, variant.pre_cond_na)))
- for post_cond, pre_conds in sorted(entries.items(),
- key=lambda x: (x[0][0], len(x[1]))):
- pre_conds = _compact_more(_compact(pre_conds))
- yield post_cond, pre_conds
-
- def _post_process(self) -> None:
- for map_idx, transitions in enumerate(self):
- if not transitions or not isinstance(
- transitions[0].enabled_by,
- bool) or not transitions[0].enabled_by:
- raise ValueError(
- f"transition map of {self._item.spec} contains no default "
- "entry for pre-condition set "
- f"{{{self._map_index_to_pre_conditions(map_idx)}}}")
- entry = self._entries.setdefault(transitions.key,
- [0, 0, transitions, []])
- entry[0] += 1
- entry[3].append(map_idx)
- for index, entry in enumerate(
- sorted(self._entries.values(),
- key=lambda x: x[0],
- reverse=True)):
- entry[1] = index
-
- def _map_index_to_pre_conditions(self, map_idx: int) -> str:
- conditions = []
- for condition in reversed(self._item["pre-conditions"]):
- states = condition["states"]
- count = len(states)
- st_idx = int(map_idx % count)
- conditions.append(f"{condition['name']}={states[st_idx]['name']}")
- map_idx //= count
- return ", ".join(reversed(conditions))
-
- def map_idx_to_pre_co_states(
- self, map_idx: int, pre_cond_na: Tuple[int,
- ...]) -> Tuple[int, ...]:
- """
- Maps the transition map index and the associated pre-condition state
- indices.
- """
- co_states = []
- for condition in reversed(self._item["pre-conditions"]):
- count = len(condition["states"])
- co_states.append(count if pre_cond_na[self._pre_co_name_to_co_idx[
- condition["name"]]] else int(map_idx % count))
- map_idx //= count
- return tuple(reversed(co_states))
-
- def pre_co_name_to_co_idx(self, co_name: str) -> int:
- """
- Maps the pre-condition name to the associated pre-condition index.
- """
- return self._pre_co_name_to_co_idx[co_name]
-
- def pre_co_idx_to_co_name(self, co_idx: int) -> str:
- """
- Maps the pre-condition index to the associated pre-condition name.
- """
- return self._pre_co_idx_to_cond[co_idx]["name"]
-
- def post_co_name_to_co_idx(self, co_name: str) -> int:
- """
- Maps the post-condition name to the associated post-condition index.
- """
- return self._post_co_name_to_co_idx[co_name]
-
- def post_co_idx_to_co_name(self, co_idx: int) -> str:
- """
- Maps the post-condition index to the associated post-condition name.
- """
- return self._post_co_idx_to_co_name[co_idx]
-
- def pre_co_idx_st_idx_to_st_name(self, co_idx: int, st_idx: int) -> str:
- """
- Maps the pre-condition name and state index to the associated state
- name.
- """
- return self._pre_co_idx_st_idx_to_st_name[co_idx][st_idx]
-
- def post_co_idx_st_idx_to_st_name(self, co_idx: int, st_idx: int) -> str:
- """
- Maps the post-condition name and state index to the associated state
- name.
- """
- return self._post_co_idx_st_idx_to_st_name[co_idx][st_idx]
-
- def pre_co_idx_st_name_to_st_idx(self, co_idx: int, st_name: str) -> int:
- """
- Maps the pre-condition index and state name to the associated state
- index.
- """
- return self._pre_co_idx_st_name_to_st_idx[co_idx][st_name]
-
- def post_co_idx_st_name_to_st_idx(self, co_idx: int, st_name: str) -> int:
- """
- Maps the post-condition index and state name to the associated state
- index.
- """
- return self._post_co_idx_st_name_to_st_idx[co_idx][st_name]
-
- def skip_idx_to_name(self, skip_idx: int) -> str:
- """
- Maps the skip index the associated skip name index.
- """
- return self._skip_idx_to_name[skip_idx]
-
- def _map_post_cond(self, map_idx: int, co_idx: int,
- variant: Transition) -> Transition:
- if isinstance(variant.post_cond[co_idx], int):
- return variant
- pre_co_states = self.map_idx_to_pre_co_states(map_idx,
- variant.pre_cond_na)
- for ops in variant.post_cond[co_idx]:
- idx = _POST_COND_OP[next(iter(ops))](_PostCondContext(
- self, map_idx, pre_co_states, variant.post_cond, co_idx, ops))
- if idx is not None:
- return Transition(
- variant.desc_idx, variant.enabled_by, variant.skip,
- variant.pre_cond_na, variant.post_cond[0:co_idx] +
- (idx, ) + variant.post_cond[co_idx + 1:])
- raise ValueError(
- "cannot determine state for post-condition "
- f"'{self._post_co_idx_to_co_name[co_idx]}' of transition map "
- f"descriptor {variant.desc_idx} of {self._item.spec} for "
- "pre-condition set "
- f"{{{self._map_index_to_pre_conditions(map_idx)}}}")
-
- def _make_post_cond(self, map_idx: int, variant: Transition) -> Transition:
- for co_idx in range(len(variant.post_cond)):
- variant = self._map_post_cond(map_idx, co_idx, variant)
- return variant
-
- def _add_variant(self, transition_map: _TransitionMap, map_idx: int,
- variant: Transition) -> None:
- if transition_map[map_idx]:
- for index, existing in enumerate(transition_map[map_idx].variants):
- if existing.enabled_by == variant.enabled_by:
- if variant.skip:
- # Allow transition map variants with a skip reason to
- # overwrite existing variants with the same enabled-by
- # attribute. This is important if variants use N/A for
- # some pre-conditions. It makes it also easier to skip
- # pre-conditon states which are controlled by build
- # options.
- transition_map[map_idx].replace(index, variant)
- return
- raise ValueError(
- f"transition map descriptor {variant.desc_idx} of "
- f"{self._item.spec} duplicates pre-condition set "
- f"{{{self._map_index_to_pre_conditions(map_idx)}}}"
- " defined by transition map descriptor "
- f"{existing.desc_idx}")
- default = transition_map[map_idx][0]
- if (default.post_cond, default.skip,
- default.pre_cond_na) == (variant.post_cond, variant.skip,
- variant.pre_cond_na):
- return
- elif not isinstance(variant.enabled_by,
- bool) or not variant.enabled_by:
- raise ValueError(
- f"transition map descriptor {variant.desc_idx} of "
- f"{self._item.spec} is the first variant for "
- f"{{{self._map_index_to_pre_conditions(map_idx)}}} "
- "and it is not enabled by default")
- self.pre_co_summary = tuple(
- a + b for a, b in zip(self.pre_co_summary, (variant.skip, ) +
- variant.pre_cond_na))
- transition_map[map_idx].add(variant)
-
- def _add_transitions(self, transition_map: _TransitionMap,
- desc: Dict[str, Any], desc_idx: int,
- skip_post_cond: Tuple[Any, ...], co_idx: int,
- map_idx: int, pre_cond_na: Tuple[int, ...]) -> None:
- # pylint: disable=too-many-arguments
- # pylint: disable=too-many-locals
- if co_idx < self._pre_co_count:
- condition = self._pre_co_idx_to_cond[co_idx]
- state_count = len(condition["states"])
- map_idx *= state_count
- states = desc["pre-conditions"][condition["name"]]
- if isinstance(states, str):
- assert states in ["all", "N/A"]
- for st_idx in range(state_count):
- self._add_transitions(
- transition_map, desc, desc_idx, skip_post_cond,
- co_idx + 1, map_idx + st_idx,
- pre_cond_na + (int(states == "N/A"), ))
- else:
- for st_name in states:
- try:
- st_idx = self._pre_co_idx_st_name_to_st_idx[co_idx][
- st_name]
- except KeyError as err:
- msg = (f"transition map descriptor {desc_idx} of "
- f"{self._item.spec} refers to non-existent "
- f"state {err} of pre-condition "
- f"'{condition['name']}'")
- raise ValueError(msg) from err
- self._add_transitions(transition_map, desc, desc_idx,
- skip_post_cond, co_idx + 1,
- map_idx + st_idx,
- pre_cond_na + (0, ))
- else:
- variant = self._make_post_cond(
- map_idx,
- Transition(desc_idx, desc["enabled-by"], skip_post_cond[0],
- pre_cond_na, skip_post_cond[1:]))
- self._add_variant(transition_map, map_idx, variant)
-
- def _add_default(self, transition_map: _TransitionMap, desc: Dict[str,
- Any],
- desc_idx: int, skip_post_cond: Tuple[int, ...]) -> None:
- enabled_by = desc["enabled-by"]
- for map_idx, transition in enumerate(transition_map):
- if not transition:
- transition.add(
- self._make_post_cond(
- map_idx,
- Transition(desc_idx, enabled_by, skip_post_cond[0],
- (0, ) * self._pre_co_count,
- skip_post_cond[1:])))
-
- def _get_post_cond(self, desc: Dict[str, Any], co_idx: int) -> Any:
- info = desc["post-conditions"][self._post_co_idx_to_co_name[co_idx]]
- if isinstance(info, str):
- return self._post_co_idx_st_name_to_st_idx[co_idx][info]
- return info
-
- def _build_map(self) -> _TransitionMap:
- transition_count = 1
- for condition in self["pre-conditions"]:
- state_count = len(condition["states"])
- if state_count == 0:
- raise ValueError(f"pre-condition '{condition['name']}' of "
- f"{self._item.spec} has no states")
- transition_count *= state_count
- transition_map = [_TransitionEntry() for _ in range(transition_count)]
- for desc_idx, desc in enumerate(self["transition-map"]):
- if isinstance(desc["post-conditions"], dict):
- try:
- skip_post_cond = (0, ) + tuple(
- self._get_post_cond(desc, co_idx)
- for co_idx in range(self._post_co_count))
- except KeyError as err:
- msg = (f"transition map descriptor {desc_idx} of "
- f"{self._item.spec} refers to non-existent "
- f"post-condition state {err}")
- raise ValueError(msg) from err
- else:
- skip_post_cond = (
- self._skip_name_to_idx[desc["post-conditions"]], ) + tuple(
- self._post_co_idx_st_name_to_st_idx[co_idx]["N/A"]
- for co_idx in range(self._post_co_count))
- if isinstance(desc["pre-conditions"], dict):
- self._add_transitions(transition_map, desc, desc_idx,
- skip_post_cond, 0, 0, ())
- else:
- assert desc["pre-conditions"] == "default"
- self._add_default(transition_map, desc, desc_idx,
- skip_post_cond)
- return transition_map
-
- def _get_entry(self, ident: str, variant: Transition) -> str:
- text = "{ " + ", ".join(
- itertools.chain(
- map(str, (int(variant.skip != 0), ) + variant.pre_cond_na),
- ((f"{ident}_Post_{self._post_co_idx_to_co_name[co_idx]}"
- f"_{self._post_co_idx_st_idx_to_st_name[co_idx][st_idx]}")
- for co_idx, st_idx in enumerate(variant.post_cond))))
- wrapper = textwrap.TextWrapper()
- wrapper.initial_indent = " "
- wrapper.subsequent_indent = " "
- wrapper.width = 79
- return "\n".join(wrapper.wrap(text)) + " },"
-
- def _get_entry_bits(self) -> int:
- bits = self._pre_co_count + 1
- for st_idx_to_st_name in self._post_co_idx_st_idx_to_st_name:
- bits += math.ceil(math.log2(len(st_idx_to_st_name)))
- return 2**max(math.ceil(math.log2(bits)), 3)
-
- def add_map(self, content: CContent, ident: str) -> None:
- """ Adds the transition map definitions to the content. """
- entries = []
- mapper = ExpressionMapper()
- for entry in self.entries():
- transitions = entry[2]
- if len(transitions) == 1:
- entries.append(self._get_entry(ident, transitions[0]))
- else:
- ifelse = "#if "
- enumerators = [] # type: List[str]
- for variant in transitions[1:]:
- enumerators.append(
- ifelse + enabled_by_to_exp(variant.enabled_by, mapper))
- enumerators.append(self._get_entry(ident, variant))
- ifelse = "#elif "
- enumerators.append("#else")
- enumerators.append(self._get_entry(ident, transitions[0]))
- enumerators.append("#endif")
- entries.append("\n".join(enumerators))
- bits = self._get_entry_bits()
- content.add("typedef struct {")
- with content.indent():
- content.append(f"uint{bits}_t Skip : 1;")
- for condition in self["pre-conditions"]:
- content.append(f"uint{bits}_t Pre_{condition['name']}_NA : 1;")
- for condition in self["post-conditions"]:
- state_bits = math.ceil(math.log2(len(condition["states"]) + 1))
- content.append(
- f"uint{bits}_t Post_{condition['name']} : {state_bits};")
- content.add(f"}} {ident}_Entry;")
- content.add([f"static const {ident}_Entry", f"{ident}_Entries[] = {{"])
- entries[-1] = entries[-1].replace("},", "}")
- content.append(entries)
- bits = max(8, math.ceil(math.log2(len(self._entries)) / 8) * 8)
- content.append(
- ["};", "", f"static const uint{bits}_t", f"{ident}_Map[] = {{"])
- text = ", ".join(
- str(self._entries[transitions.key][1])
- for transitions in self._map)
- wrapper = textwrap.TextWrapper()
- wrapper.initial_indent = " "
- wrapper.subsequent_indent = " "
- wrapper.width = 79
- content.append(wrapper.wrap(text))
- content.append("};")
-
- def get_post_entry_member(self, co_idx: int) -> str:
- """
- Gets the post-condition entry member name for the post-condition index.
- """
- return f"Post_{self._post_co_idx_to_co_name[co_idx]}"
def _to_enum(prefix: str, conditions: List[Any]) -> _IdxToX:
diff --git a/specview.py b/specview.py
index d834b3e2..86dc20eb 100755
--- a/specview.py
+++ b/specview.py
@@ -34,7 +34,7 @@ from rtemsspec.items import EmptyItem, Item, ItemCache, ItemMapper, \
ItemGetValueContext, Link
from rtemsspec.sphinxcontent import SphinxContent
from rtemsspec.util import load_config
-from rtemsspec.validation import Transition, TransitionMap
+from rtemsspec.transitionmap import Transition, TransitionMap
_CHILD_ROLES = [
"requirement-refinement", "interface-ingroup", "interface-ingroup-hidden",