# Upstream repository: <https://github.com/efficios/normand>.
__author__ = "Philippe Proulx"
-__version__ = "0.5.0"
+__version__ = "0.6.0"
__all__ = [
"ByteOrder",
"parse",
)
-# Fixed-length integer, possibly needing more than one byte.
-class _FlInt(_ScalarItem, _RepableItem, _ExprMixin):
+# Fixed-length number, possibly needing more than one byte.
+class _FlNum(_ScalarItem, _RepableItem, _ExprMixin):
def __init__(
self, expr_str: str, expr: ast.Expression, len: int, text_loc: TextLoc
):
return self._len // 8
def __repr__(self):
- return "_FlInt({}, {}, {}, {})".format(
+ return "_FlNum({}, {}, {}, {})".format(
repr(self._expr_str), repr(self._expr), repr(self._len), self._text_loc
)
# Expression item type.
-_ExprItemT = Union[_FlInt, _Leb128Int, _VarAssign, _Rep]
+_ExprItemT = Union[_FlNum, _Leb128Int, _VarAssign, _Rep]
# A parsing error containing a message and a text location.
return expr_str, expr
- # Patterns for _try_parse_val_and_attr()
+ # Patterns for _try_parse_num_and_attr()
_val_expr_pat = re.compile(r"([^}:]+):\s*")
- _fl_int_len_attr_pat = re.compile(r"8|16|24|32|40|48|56|64")
+ _fl_num_len_attr_pat = re.compile(r"8|16|24|32|40|48|56|64")
_leb128_int_attr_pat = re.compile(r"(u|s)leb128")
# Tries to parse a value and attribute (fixed length in bits or
# `leb128`), returning a value item on success.
- def _try_parse_val_and_attr(self):
+ def _try_parse_num_and_attr(self):
begin_text_loc = self._text_loc
# Match
expr_str, expr = self._ast_expr_from_str(m_expr.group(1), begin_text_loc)
# Length?
- m_attr = self._try_parse_pat(self._fl_int_len_attr_pat)
+ m_attr = self._try_parse_pat(self._fl_num_len_attr_pat)
if m_attr is None:
# LEB128?
cls = _ULeb128Int if m_attr.group(1) == "u" else _SLeb128Int
return cls(expr_str, expr, begin_text_loc)
else:
- # Return fixed-length integer item
- return _FlInt(
+ # Return fixed-length number item
+ return _FlNum(
expr_str,
expr,
int(m_attr.group(0)),
begin_text_loc,
)
- # Patterns for _try_parse_val_and_attr()
+ # Patterns for _try_parse_num_and_attr()
_var_assign_pat = re.compile(
r"(?P<name>{})\s*=\s*(?P<expr>[^}}]+)".format(_py_name_pat.pattern)
)
item = self._try_parse_var_assign()
if item is None:
- # Fixed-length value item?
- item = self._try_parse_val_and_attr()
+ # Number item?
+ item = self._try_parse_num_and_attr()
if item is None:
# Byte order setting item?
if item is None:
# At this point it's invalid
self._raise_error(
- "Expecting a fixed-length integer, a variable assignment, or a byte order setting"
+ "Expecting a fixed-length number, a variable assignment, or a byte order setting"
)
# Expect suffix
#
# If `allow_icitte` is `True`, then the `ICITTE` name is available
# for the expression to evaluate.
+ #
+ # If `allow_float` is `True`, then the type of the result may be
+ # `float` too.
@staticmethod
- def _eval_item_expr(item: _ExprItemT, state: _GenState, allow_icitte: bool):
+ def _eval_item_expr(
+ item: _ExprItemT,
+ state: _GenState,
+ allow_icitte: bool,
+ allow_float: bool = False,
+ ):
syms = state.labels.copy()
# Set the `ICITTE` name to the current offset, if any
item,
)
- # Validate result
- if type(val) is not int:
+ # Validate result type
+ expected_types = {int} # type: Set[type]
+ type_msg = "`int`"
+
+ if allow_float:
+ expected_types.add(float)
+ type_msg += " or `float`"
+
+ if type(val) not in expected_types:
_raise_error_for_item(
- "Invalid expression `{}`: expecting result type `int`, not `{}`".format(
- item.expr_str, type(val).__name__
+ "Invalid expression `{}`: expecting result type {}, not `{}`".format(
+ item.expr_str, type_msg, type(val).__name__
),
item,
)
if do_eval:
# Evaluate the expression and keep the result
- state.variables[item.name] = _Gen._eval_item_expr(item, state, True)
+ state.variables[item.name] = _Gen._eval_item_expr(
+ item, state, True, True
+ )
elif type(item) is _SetOffset:
state.offset = item.val
elif isinstance(item, _Leb128Int):
return next_vl_instance
# Handles the fixed-length integer item `item`.
- def _handle_fl_int_item(
- self, item: _FlInt, state: _GenState, next_vl_instance: int
- ):
- # Compute value
- val = self._eval_item_expr(item, state, True)
-
+ def _handle_fl_int_item(self, val: int, item: _FlNum, state: _GenState):
# Validate range
if val < -(2 ** (item.len - 1)) or val > 2**item.len - 1:
_raise_error_for_item(
# Encode result on 64 bits (to extend the sign bit whatever the
# value of `item.len`).
- if state.bo is None and item.len > 8:
- _raise_error_for_item(
- "Current byte order isn't defined at first value (`{}`) to encode on more than 8 bits".format(
- item.expr_str
- ),
- item,
- )
-
data = struct.pack(
"{}{}".format(
">" if state.bo in (None, ByteOrder.BE) else "<",
# Append to current bytes and update offset
self._data += data
- state.offset += len(data)
+
+ # Handles the fixed-length integer item `item`.
+ def _handle_fl_float_item(self, val: float, item: _FlNum, state: _GenState):
+ # Validate length
+ if item.len not in (32, 64):
+ _raise_error_for_item(
+ "Invalid {}-bit length for a fixed-length floating point number (value {:,})".format(
+ item.len, val
+ ),
+ item,
+ )
+
+ # Encode result
+ self._data += struct.pack(
+ "{}{}".format(
+ ">" if state.bo in (None, ByteOrder.BE) else "<",
+ "f" if item.len == 32 else "d",
+ ),
+ val,
+ )
+
+ # Handles the fixed-length number item `item`.
+ def _handle_fl_num_item(
+ self, item: _FlNum, state: _GenState, next_vl_instance: int
+ ):
+ # Compute value
+ val = self._eval_item_expr(item, state, True, True)
+
+ # Validate current byte order
+ if state.bo is None and item.len > 8:
+ _raise_error_for_item(
+ "Current byte order isn't defined at first fixed-length number (`{}`) to encode on more than 8 bits".format(
+ item.expr_str
+ ),
+ item,
+ )
+
+ # Handle depending on type
+ if type(val) is int:
+ self._handle_fl_int_item(val, item, state)
+ else:
+ assert type(val) is float
+ self._handle_fl_float_item(val, item, state)
+
+ # Update offset
+ state.offset += item.size
+
return next_vl_instance
# Handles the LEB128 integer item `item`.
# Item handlers
self._item_handlers = {
_Byte: self._handle_byte_item,
- _FlInt: self._handle_fl_int_item,
+ _FlNum: self._handle_fl_num_item,
_Group: self._handle_group_item,
_Label: self._handle_label_item,
_Rep: self._handle_rep_item,
# Item size getters
self._item_size_funcs = {
_Byte: self._scalar_item_size,
- _FlInt: self._scalar_item_size,
+ _FlNum: self._scalar_item_size,
_Group: self._group_item_size,
_Label: self._zero_item_size,
_Rep: self._rep_item_size,