+ # Computes the effective value (multiplier) for each repetition
+ # instance, filling `instance_vals` (if not `None`) and returning
+ # `instance_vals`.
+ #
+ # At this point it must be known that, for a given repetition, its
+ # expression only contains reachable names.
+ #
+ # When handling a `_Rep` item, this function appends its effective
+ # multiplier to `instance_vals` _before_ handling its repeated item.
+ #
+ # When handling a `_VarAssign` item, this function only evaluates it if
+ # all its names are reachable.
+ @staticmethod
+ def _compute_rep_instance_vals(
+ item: _Item, state: _GenState, instance_vals: Optional[List[int]] = None
+ ):
+ if instance_vals is None:
+ instance_vals = []
+
+ if isinstance(item, _ScalarItem):
+ state.offset += item.size
+ elif type(item) is _Label:
+ state.labels[item.name] = state.offset
+ elif type(item) is _VarAssign:
+ # Check if all the names are reachable
+ do_eval = True
+
+ for name in _Gen._names_of_expr(item.expr):
+ if (
+ name != _icitte_name
+ and name not in state.variables
+ and name not in state.labels
+ ):
+ # A name is unknown: cannot evaluate
+ do_eval = False
+ break
+
+ if do_eval:
+ # Evaluate the expression and keep the result
+ state.variables[item.name] = _Gen._eval_item_expr(item, state, True)
+ elif type(item) is _SetOffset:
+ state.offset = item.val
+ elif type(item) is _Rep:
+ # Evaluate the expression and keep the result
+ val = _Gen._eval_item_expr(item, state, False)
+
+ # Validate result
+ if val < 0:
+ _raise_error_for_item(
+ "Invalid expression `{}`: unexpected negative result {:,}".format(
+ item.expr_str, val
+ ),
+ item,
+ )
+
+ # Add to repetition instance values
+ instance_vals.append(val)
+
+ # Process the repeated item `val` times
+ for _ in range(val):
+ _Gen._compute_rep_instance_vals(item.item, state, instance_vals)
+ elif type(item) is _Group:
+ prev_labels = state.labels.copy()
+
+ # Process each item
+ for subitem in item.items:
+ _Gen._compute_rep_instance_vals(subitem, state, instance_vals)
+
+ state.labels = prev_labels
+
+ return instance_vals
+
+ def _zero_item_size(self, item: _Item, next_rep_instance: int):
+ return 0, next_rep_instance
+
+ def _scalar_item_size(self, item: _ScalarItem, next_rep_instance: int):
+ return item.size, next_rep_instance
+
+ def _group_item_size(self, item: _Group, next_rep_instance: int):
+ size = 0
+
+ for subitem in item.items:
+ subitem_size, next_rep_instance = self._item_size(
+ subitem, next_rep_instance
+ )
+ size += subitem_size
+
+ return size, next_rep_instance
+
+ def _rep_item_size(self, item: _Rep, next_rep_instance: int):
+ # Get the value from `self._rep_instance_vals` _before_
+ # incrementing `next_rep_instance` to honor the order of
+ # _compute_rep_instance_vals().
+ mul = self._rep_instance_vals[next_rep_instance]
+ next_rep_instance += 1
+ size = 0
+
+ for _ in range(mul):
+ iter_size, next_rep_instance = self._item_size(item.item, next_rep_instance)
+ size += iter_size
+
+ return size, next_rep_instance
+
+ # Returns the size of `item` and the new next repetition instance.
+ def _item_size(self, item: _Item, next_rep_instance: int):
+ return self._item_size_funcs[type(item)](item, next_rep_instance)
+
+ # Handles the byte item `item`.
+ def _handle_byte_item(self, item: _Byte, state: _GenState, next_rep_instance: int):
+ self._data.append(item.val)
+ state.offset += item.size
+ return next_rep_instance
+
+ # Handles the string item `item`.
+ def _handle_str_item(self, item: _Str, state: _GenState, next_rep_instance: int):
+ self._data += item.data
+ state.offset += item.size
+ return next_rep_instance
+
+ # Handles the byte order setting item `item`.
+ def _handle_set_bo_item(
+ self, item: _SetBo, state: _GenState, next_rep_instance: int
+ ):
+ # Update current byte order
+ state.bo = item.bo
+ return next_rep_instance
+
+ # Handles the variable assignment item `item`.
+ def _handle_var_assign_item(
+ self, item: _VarAssign, state: _GenState, next_rep_instance: int
+ ):