Add fixed-length floating point number support v0.6.0
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 29 Sep 2023 19:30:49 +0000 (15:30 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 29 Sep 2023 19:38:08 +0000 (15:38 -0400)
This patch adds support to encode binary32 and binary64 (as per
IEEE 754-2008) floating point numbers, big and little endian.

The Normand form is the same as a fixed-length integer:

    { 32.87 : 32 }

The type of the result of the evaluation determines how to encode it:
integer or floating point number.

Some "fixed-length integer" terms are changed to "fixed-length number"
when they're meant to be general.

With this patch, you may also assign a variable to a floating point
number value. However, the result type of an LEB128 integer or
repetition count expression must still be `int`.

Change-Id: Ic13ed747276a365ed2fd8854545ab2b0e1c343a0
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
45 files changed:
README.adoc
normand/normand.py
pyproject.toml
tests/fail-fl-float-inval-len.nt [new file with mode: 0644]
tests/fail-fl-int-32b-out-of-range-pos.nt
tests/fail-fl-int-empty-expr.nt [deleted file]
tests/fail-fl-int-eval-type.nt [deleted file]
tests/fail-fl-int-eval.nt [deleted file]
tests/fail-fl-int-inval-len.nt [deleted file]
tests/fail-fl-int-inval-syntax.nt [deleted file]
tests/fail-fl-int-missing-bo.nt [deleted file]
tests/fail-fl-int-missing-len.nt [deleted file]
tests/fail-fl-int-unknown-name-1.nt [deleted file]
tests/fail-fl-int-unknown-name-2.nt [deleted file]
tests/fail-fl-num-empty-expr.nt [new file with mode: 0644]
tests/fail-fl-num-eval-type.nt [new file with mode: 0644]
tests/fail-fl-num-eval.nt [new file with mode: 0644]
tests/fail-fl-num-inval-len.nt [new file with mode: 0644]
tests/fail-fl-num-inval-syntax.nt [new file with mode: 0644]
tests/fail-fl-num-missing-bo.nt [new file with mode: 0644]
tests/fail-fl-num-missing-len.nt [new file with mode: 0644]
tests/fail-fl-num-unknown-name-1.nt [new file with mode: 0644]
tests/fail-fl-num-unknown-name-2.nt [new file with mode: 0644]
tests/fail-rep-eval-type-1.nt [new file with mode: 0644]
tests/fail-rep-eval-type-2.nt [new file with mode: 0644]
tests/fail-rep-eval-type.nt [deleted file]
tests/fail-sleb128-empty-expr.nt
tests/fail-uleb128-empty-expr.nt
tests/fail-uleb128-eval-type-1.nt [new file with mode: 0644]
tests/fail-uleb128-eval-type-2.nt [new file with mode: 0644]
tests/fail-uleb128-eval-type.nt [deleted file]
tests/fail-var-eval-type.nt
tests/fail-var-label-bo-none.nt
tests/pass-fl-float-32b-be.nt [new file with mode: 0644]
tests/pass-fl-float-32b-le.nt [new file with mode: 0644]
tests/pass-fl-float-64b-be.nt [new file with mode: 0644]
tests/pass-fl-float-64b-le.nt [new file with mode: 0644]
tests/pass-readme-intro-fl-num.nt [new file with mode: 0644]
tests/pass-readme-learn-fl-int-1.nt [deleted file]
tests/pass-readme-learn-fl-int-2.nt [deleted file]
tests/pass-readme-learn-fl-int-3.nt [deleted file]
tests/pass-readme-learn-fl-num-1.nt [new file with mode: 0644]
tests/pass-readme-learn-fl-num-2.nt [new file with mode: 0644]
tests/pass-readme-learn-fl-num-3.nt [new file with mode: 0644]
tests/pass-readme-learn-fl-num-4.nt [new file with mode: 0644]

index 72c2f82b8e61890162af90d4f65d7fd798f7e51a..44e62c513f90f599a86a4d58335974fdfb4fa24b 100644 (file)
@@ -29,7 +29,7 @@ _**Normand**_ is a text-to-binary processor with its own language.
 This package offers both a portable {py3} module and a command-line
 tool.
 
-WARNING: This version of Normand is 0.5, meaning both the Normand
+WARNING: This version of Normand is 0.6, meaning both the Normand
 language and the module/CLI interface aren't stable.
 
 ifdef::env-github[]
@@ -133,7 +133,7 @@ Variables::
 The value of a variable assignment is the evaluation of a valid {py3}
 expression which may include label and variable names.
 
-Fixed-length integer with a given length (8{nbsp}bits to 64{nbsp}bits) and byte order::
+Fixed-length number with a given length (8{nbsp}bits to 64{nbsp}bits) and byte order::
 +
 Input:
 +
@@ -141,15 +141,17 @@ Input:
 {strength = 4}
 {be} 67 <lbl> 44 $178 {(end - lbl) * 8 + strength : 16} $99 <end>
 {le} {-1993 : 32}
+{-3.141593 : 64}
 ----
 +
 Output:
 +
 ----
-67 44 b2 00 2c 63 37 f8  ff ff
+67 44 b2 00 2c 63 37 f8  ff ff 7f bd c2 82 fb 21
+09 c0
 ----
 +
-The encoded integer is the evaluation of a valid {py3} expression which
+The encoded number is the evaluation of a valid {py3} expression which
 may include label and variable names.
 
 https://en.wikipedia.org/wiki/LEB128[LEB128] integer::
@@ -276,8 +278,8 @@ current state:
 |[[cur-offset]] Current offset
 |
 The current offset has an effect on the value of <<label,labels>> and of
-the special `ICITTE` name in <<fixed-length-integer,fixed-length
-integer>>, <<leb-128-integer,LEB128 integer>>, and
+the special `ICITTE` name in <<fixed-length-number,fixed-length
+number>>, <<leb-128-integer,LEB128 integer>>, and
 <<variable-assignment,variable assignment>> expression evaluation.
 
 Each generated byte increments the current offset.
@@ -290,7 +292,7 @@ current offset.
 |[[cur-bo]] Current byte order
 |
 The current byte order has an effect on the encoding of
-<<fixed-length-integer,fixed-length integers>>.
+<<fixed-length-number,fixed-length numbers>>.
 
 A <<current-byte-order-setting,current byte order setting>> may change
 the current byte order.
@@ -318,9 +320,9 @@ The available items are:
 * A <<current-byte-order-setting,current byte order setting>> (big or
   little endian).
 
-* A <<fixed-length-integer,fixed-length integer>> using the
-  <<cur-bo,current byte order>> and of which the value is the result of
-  a {py3} expression.
+* A <<fixed-length-number,fixed-length number>> (integer or
+  floating point) using the <<cur-bo,current byte order>> and of which
+  the value is the result of a {py3} expression.
 
 * An <<leb128-integer,LEB128 integer>> of which the value is the result
   of a {py3} expression.
@@ -556,31 +558,49 @@ The two accepted forms are:
 ``pass:[{be}]``:: Set the current byte order to big endian.
 ``pass:[{le}]``:: Set the current byte order to little endian.
 
-=== Fixed-length integer
+=== Fixed-length number
 
-A _fixed-length integer_ represents a fixed number of bytes encoding an
-unsigned or signed integer which is the result of evaluating a {py3}
-expression using the <<cur-bo,current byte order>>.
+A _fixed-length number_ represents a fixed number of bytes encoding
+either:
+
+* An unsigned or signed integer (two's complement).
++
+The available lengths are 8, 16, 24, 32, 40, 48, 56, and 64.
+
+* A floating point number
+  ([IEEE{nbsp}754-2008[https://standards.ieee.org/standard/754-2008.html]).
++
+The available length are 32 (_binary32_) and 64 (_binary64_).
 
-A fixed-length integer is:
+The value is the result of evaluating a {py3} expression using the
+<<cur-bo,current byte order>>.
+
+A fixed-length number is:
 
 . The ``pass:[{]`` prefix.
 
 . A valid {py3} expression.
 +
-For a fixed-length integer at some source location{nbsp}__**L**__, this
+For a fixed-length number at some source location{nbsp}__**L**__, this
 expression may contain the name of any accessible <<label,label>> (not
 within a nested group), including the name of a label defined
 after{nbsp}__**L**__, as well as the name of any
 <<variable-assignment,variable>> known at{nbsp}__**L**__.
 +
-The value of the special name `ICITTE` in this expression is the
-<<cur-offset,current offset>> (before encoding the integer).
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>> (before encoding the number).
 
 . The `:` character.
 
-. An encoding length in bits amongst `8`, `16`, `24`, `32`, `40`,
-  `48`, `56`, and `64`.
+. An encoding length in bits amongst:
++
+--
+The expression evaluates to an `int` value::
+    `8`, `16`, `24`, `32`, `40`, `48`, `56`, and `64`.
+
+The expression evaluates to a `float` value::
+    `32` and `64`.
+--
 
 . The `}` suffix.
 
@@ -635,6 +655,21 @@ Output:
 ----
 ====
 
+====
+Input:
+
+----
+{le}
+{2 * 0.0529 : 32}
+----
+
+Output:
+
+----
+ac ad d8 3d
+----
+====
+
 === LEB128 integer
 
 An _LEB128 integer_ represents a variable number of bytes encoding an
@@ -658,8 +693,8 @@ expression may contain:
   defined after{nbsp}__**L**__.
 --
 +
-The value of the special name `ICITTE` in this expression is the
-<<cur-offset,current offset>> (before encoding the integer).
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>> (before encoding the integer).
 
 . The `:` character.
 
@@ -763,7 +798,7 @@ A label is:
 . The `<` prefix.
 
 . A valid {py3} name which is not `ICITTE` (see
-  <<fixed-length-integer>>, <<leb128-integer>>, and
+  <<fixed-length-number>>, <<leb128-integer>>, and
   <<variable-assignment>> to learn more).
 
 . The `>` suffix.
@@ -778,7 +813,7 @@ A variable assignment is:
 . The ``pass:[{]`` prefix.
 
 . A valid {py3} name which is not `ICITTE` (see
-  <<fixed-length-integer>>, <<leb128-integer>>, and
+  <<fixed-length-number>>, <<leb128-integer>>, and
   <<variable-assignment>> to learn more).
 
 . The `=` character.
@@ -791,8 +826,8 @@ within a nested group), including the name of a label defined
 after{nbsp}__**L**__, as well as the name of any
 <<variable-assignment,variable>> known at{nbsp}__**L**__.
 +
-The value of the special name `ICITTE` in this expression is the
-<<cur-offset,current offset>>.
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>>.
 
 . The `}` suffix.
 
index d84dce41afdbf936992dbf9297b69fca090944d3..c643429012be389d2433df9a2db687e00d7fb71b 100644 (file)
@@ -30,7 +30,7 @@
 # Upstream repository: <https://github.com/efficios/normand>.
 
 __author__ = "Philippe Proulx"
-__version__ = "0.5.0"
+__version__ = "0.6.0"
 __all__ = [
     "ByteOrder",
     "parse",
@@ -246,8 +246,8 @@ class _VarAssign(_Item, _ExprMixin):
         )
 
 
-# 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
     ):
@@ -265,7 +265,7 @@ class _FlInt(_ScalarItem, _RepableItem, _ExprMixin):
         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
         )
 
@@ -331,7 +331,7 @@ class _Rep(_Item, _ExprMixin):
 
 
 # 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.
@@ -655,14 +655,14 @@ class _Parser:
 
         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
@@ -676,7 +676,7 @@ class _Parser:
         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?
@@ -692,15 +692,15 @@ class _Parser:
             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)
     )
@@ -780,8 +780,8 @@ class _Parser:
         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?
@@ -790,7 +790,7 @@ class _Parser:
                 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
@@ -1327,8 +1327,16 @@ class _Gen:
     #
     # 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
@@ -1350,11 +1358,18 @@ class _Gen:
                 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,
             )
@@ -1420,7 +1435,9 @@ class _Gen:
 
             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):
@@ -1545,12 +1562,7 @@ class _Gen:
         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(
@@ -1562,14 +1574,6 @@ class _Gen:
 
         # 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 "<",
@@ -1591,7 +1595,53 @@ class _Gen:
 
         # 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`.
@@ -1695,7 +1745,7 @@ class _Gen:
         # 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,
@@ -1710,7 +1760,7 @@ class _Gen:
         # 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,
index ac3849ce3525050c835e6c59226f16875a75634b..3eb704defe390c6c516d6fb8a3f2cdebacbf3213 100644 (file)
@@ -23,7 +23,7 @@
 
 [tool.poetry]
 name = 'normand'
-version = '0.5.0'
+version = '0.6.0'
 description = 'Text-to-binary processor with its own language'
 license = 'MIT'
 authors = ['Philippe Proulx <eeppeliteloop@gmail.com>']
diff --git a/tests/fail-fl-float-inval-len.nt b/tests/fail-fl-float-inval-len.nt
new file mode 100644 (file)
index 0000000..bc02979
--- /dev/null
@@ -0,0 +1,4 @@
+{be}
+{ 18.49 : 16 }
+---
+2:3 - Invalid 16-bit length for a fixed-length floating point number (value 18.49)
index 668b984c31fd809c9d69dacefe354b7faf774e95..85a89976dbc1fd773f6872e86aa05a8d8076809d 100644 (file)
@@ -1,4 +1,5 @@
+{be}
 "yoyo"
 { 2**32 : 32 }
 ---
-2:3 - Value 4,294,967,296 is outside the 32-bit range when evaluating expression `2**32` at byte offset 4
+3:3 - Value 4,294,967,296 is outside the 32-bit range when evaluating expression `2**32` at byte offset 4
diff --git a/tests/fail-fl-int-empty-expr.nt b/tests/fail-fl-int-empty-expr.nt
deleted file mode 100644 (file)
index 735c267..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ : 8 }
----
-1:3 - Expecting a fixed-length integer, a variable assignment, or a byte order setting
diff --git a/tests/fail-fl-int-eval-type.nt b/tests/fail-fl-int-eval-type.nt
deleted file mode 100644 (file)
index 5b7c642..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 'salut' : 8 }
----
-1:3 - Invalid expression `'salut'`: expecting result type `int`, not `str`
diff --git a/tests/fail-fl-int-eval.nt b/tests/fail-fl-int-eval.nt
deleted file mode 100644 (file)
index 8f503e3..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 23 + zoom(14) : 8 }
----
-1:3 - Failed to evaluate expression `23 + zoom(14)`: name 'zoom' is not defined
diff --git a/tests/fail-fl-int-inval-len.nt b/tests/fail-fl-int-inval-len.nt
deleted file mode 100644 (file)
index 7c578ef..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 23 : 17 }
----
-1:8 - Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`
diff --git a/tests/fail-fl-int-inval-syntax.nt b/tests/fail-fl-int-inval-syntax.nt
deleted file mode 100644 (file)
index a4d8af2..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 19 + / 23 : 8 }
----
-1:3 - Invalid expression `19 + / 23`: invalid syntax
diff --git a/tests/fail-fl-int-missing-bo.nt b/tests/fail-fl-int-missing-bo.nt
deleted file mode 100644 (file)
index ce68c47..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 34 : 16 }
----
-1:3 - Current byte order isn't defined at first value (`34`) to encode on more than 8 bits
diff --git a/tests/fail-fl-int-missing-len.nt b/tests/fail-fl-int-missing-len.nt
deleted file mode 100644 (file)
index d59bbe6..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 23 :  }
----
-1:9 - Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`
diff --git a/tests/fail-fl-int-unknown-name-1.nt b/tests/fail-fl-int-unknown-name-1.nt
deleted file mode 100644 (file)
index 2663bf0..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ meow : 8 }
----
-1:3 - Illegal (unknown or unreachable) variable/label name `meow` in expression `meow`; the legal names are {`ICITTE`}
diff --git a/tests/fail-fl-int-unknown-name-2.nt b/tests/fail-fl-int-unknown-name-2.nt
deleted file mode 100644 (file)
index c2c2547..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{ meow : 8 }
-{ meow = 34 }
----
-1:3 - Illegal (unknown or unreachable) variable/label name `meow` in expression `meow`; the legal names are {`ICITTE`}
diff --git a/tests/fail-fl-num-empty-expr.nt b/tests/fail-fl-num-empty-expr.nt
new file mode 100644 (file)
index 0000000..8b6b957
--- /dev/null
@@ -0,0 +1,3 @@
+{ : 8 }
+---
+1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
diff --git a/tests/fail-fl-num-eval-type.nt b/tests/fail-fl-num-eval-type.nt
new file mode 100644 (file)
index 0000000..5269ef1
--- /dev/null
@@ -0,0 +1,3 @@
+{ 'salut' : 8 }
+---
+1:3 - Invalid expression `'salut'`: expecting result type `int` or `float`, not `str`
diff --git a/tests/fail-fl-num-eval.nt b/tests/fail-fl-num-eval.nt
new file mode 100644 (file)
index 0000000..8f503e3
--- /dev/null
@@ -0,0 +1,3 @@
+{ 23 + zoom(14) : 8 }
+---
+1:3 - Failed to evaluate expression `23 + zoom(14)`: name 'zoom' is not defined
diff --git a/tests/fail-fl-num-inval-len.nt b/tests/fail-fl-num-inval-len.nt
new file mode 100644 (file)
index 0000000..7c578ef
--- /dev/null
@@ -0,0 +1,3 @@
+{ 23 : 17 }
+---
+1:8 - Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`
diff --git a/tests/fail-fl-num-inval-syntax.nt b/tests/fail-fl-num-inval-syntax.nt
new file mode 100644 (file)
index 0000000..a4d8af2
--- /dev/null
@@ -0,0 +1,3 @@
+{ 19 + / 23 : 8 }
+---
+1:3 - Invalid expression `19 + / 23`: invalid syntax
diff --git a/tests/fail-fl-num-missing-bo.nt b/tests/fail-fl-num-missing-bo.nt
new file mode 100644 (file)
index 0000000..3721baf
--- /dev/null
@@ -0,0 +1,3 @@
+{ 34 : 16 }
+---
+1:3 - Current byte order isn't defined at first fixed-length number (`34`) to encode on more than 8 bits
diff --git a/tests/fail-fl-num-missing-len.nt b/tests/fail-fl-num-missing-len.nt
new file mode 100644 (file)
index 0000000..d59bbe6
--- /dev/null
@@ -0,0 +1,3 @@
+{ 23 :  }
+---
+1:9 - Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`
diff --git a/tests/fail-fl-num-unknown-name-1.nt b/tests/fail-fl-num-unknown-name-1.nt
new file mode 100644 (file)
index 0000000..2663bf0
--- /dev/null
@@ -0,0 +1,3 @@
+{ meow : 8 }
+---
+1:3 - Illegal (unknown or unreachable) variable/label name `meow` in expression `meow`; the legal names are {`ICITTE`}
diff --git a/tests/fail-fl-num-unknown-name-2.nt b/tests/fail-fl-num-unknown-name-2.nt
new file mode 100644 (file)
index 0000000..c2c2547
--- /dev/null
@@ -0,0 +1,4 @@
+{ meow : 8 }
+{ meow = 34 }
+---
+1:3 - Illegal (unknown or unreachable) variable/label name `meow` in expression `meow`; the legal names are {`ICITTE`}
diff --git a/tests/fail-rep-eval-type-1.nt b/tests/fail-rep-eval-type-1.nt
new file mode 100644 (file)
index 0000000..c2d4e22
--- /dev/null
@@ -0,0 +1,3 @@
+77 * { [1, 2, 3] }
+---
+1:4 - Invalid expression `[1, 2, 3]`: expecting result type `int`, not `list`
diff --git a/tests/fail-rep-eval-type-2.nt b/tests/fail-rep-eval-type-2.nt
new file mode 100644 (file)
index 0000000..7fb72c7
--- /dev/null
@@ -0,0 +1,3 @@
+77 * { 3.4 }
+---
+1:4 - Invalid expression `3.4`: expecting result type `int`, not `float`
diff --git a/tests/fail-rep-eval-type.nt b/tests/fail-rep-eval-type.nt
deleted file mode 100644 (file)
index c2d4e22..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-77 * { [1, 2, 3] }
----
-1:4 - Invalid expression `[1, 2, 3]`: expecting result type `int`, not `list`
index dfe17b5703771c5dc9628662f78c7857524c45f1..c6d2cc9247b0e0b681ea3b91dfa8a5dbcafa3020 100644 (file)
@@ -1,3 +1,3 @@
 { : sleb128 }
 ---
-1:3 - Expecting a fixed-length integer, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
index a00353592afa311439ca54a42f7dca78a87446b4..fe15ea4120310c51c74af16e8b878019acfeea15 100644 (file)
@@ -1,3 +1,3 @@
 { : uleb128 }
 ---
-1:3 - Expecting a fixed-length integer, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
diff --git a/tests/fail-uleb128-eval-type-1.nt b/tests/fail-uleb128-eval-type-1.nt
new file mode 100644 (file)
index 0000000..4bf01c1
--- /dev/null
@@ -0,0 +1,3 @@
+{ 'salut' : uleb128 }
+---
+1:3 - Invalid expression `'salut'`: expecting result type `int`, not `str`
diff --git a/tests/fail-uleb128-eval-type-2.nt b/tests/fail-uleb128-eval-type-2.nt
new file mode 100644 (file)
index 0000000..7096660
--- /dev/null
@@ -0,0 +1,3 @@
+{ 45.2 : uleb128 }
+---
+1:3 - Invalid expression `45.2`: expecting result type `int`, not `float`
diff --git a/tests/fail-uleb128-eval-type.nt b/tests/fail-uleb128-eval-type.nt
deleted file mode 100644 (file)
index 4bf01c1..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{ 'salut' : uleb128 }
----
-1:3 - Invalid expression `'salut'`: expecting result type `int`, not `str`
index 73bbe12bdb2c553b3127bdc1c654343a7f00ba20..23d04d65827848144a3e83fb1d6138490bf7881e 100644 (file)
@@ -1,3 +1,3 @@
 { meow = 'salut' }
 ---
-1:3 - Invalid expression `'salut'`: expecting result type `int`, not `str`
+1:3 - Invalid expression `'salut'`: expecting result type `int` or `float`, not `str`
index ecae7c20382ecaa646c9c16296a2f75a967bc568..89f1d51724519433ac00f4a7efb6f74bbb308409 100644 (file)
@@ -1,3 +1,3 @@
 { hello }
 ---
-1:3 - Expecting a fixed-length integer, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
diff --git a/tests/pass-fl-float-32b-be.nt b/tests/pass-fl-float-32b-be.nt
new file mode 100644 (file)
index 0000000..76c27ae
--- /dev/null
@@ -0,0 +1,6 @@
+{be}
+{2.99792458e8:32}
+{-1.6021766208:32}
+---
+4d 8e f3 c2
+bf cd 14 20
diff --git a/tests/pass-fl-float-32b-le.nt b/tests/pass-fl-float-32b-le.nt
new file mode 100644 (file)
index 0000000..dab2899
--- /dev/null
@@ -0,0 +1,6 @@
+{le}
+{2.99792458e8:32}
+{-1.6021766208:32}
+---
+c2 f3 8e 4d
+20 14 cd bf
diff --git a/tests/pass-fl-float-64b-be.nt b/tests/pass-fl-float-64b-be.nt
new file mode 100644 (file)
index 0000000..825e65a
--- /dev/null
@@ -0,0 +1,6 @@
+{be}
+{2.99792458e8:64}
+{-1.6021766208:64}
+---
+41 b1 de 78 4a 00 00 00
+bf f9 a2 83 f3 cc 07 58
diff --git a/tests/pass-fl-float-64b-le.nt b/tests/pass-fl-float-64b-le.nt
new file mode 100644 (file)
index 0000000..1c44ff3
--- /dev/null
@@ -0,0 +1,6 @@
+{le}
+{2.99792458e8:64}
+{-1.6021766208:64}
+---
+00 00 00 4a 78 de b1 41
+58 07 cc f3 83 a2 f9 bf
diff --git a/tests/pass-readme-intro-fl-num.nt b/tests/pass-readme-intro-fl-num.nt
new file mode 100644 (file)
index 0000000..da03aa9
--- /dev/null
@@ -0,0 +1,7 @@
+{strength = 4}
+{be} 67 <lbl> 44 $178 {(end - lbl) * 8 + strength : 16} $99 <end>
+{le} {-1993 : 32}
+{-3.141593 : 64}
+---
+67 44 b2 00 2c 63 37 f8 ff ff 7f bd c2 82 fb 21
+09 c0
diff --git a/tests/pass-readme-learn-fl-int-1.nt b/tests/pass-readme-learn-fl-int-1.nt
deleted file mode 100644 (file)
index 63af0d7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{le} {345:16}
-{be} {-0xabcd:32}
----
-59 01 ff ff 54 33
diff --git a/tests/pass-readme-learn-fl-int-2.nt b/tests/pass-readme-learn-fl-int-2.nt
deleted file mode 100644 (file)
index 472c545..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{be}
-
-# String length in bits
-{8 * (str_end - str_beg) : 16}
-
-# String
-<str_beg>
-  "hello world!"
-<str_end>
----
-00 60 68 65 6c 6c 6f 20 77 6f 72 6c 64 21
diff --git a/tests/pass-readme-learn-fl-int-3.nt b/tests/pass-readme-learn-fl-int-3.nt
deleted file mode 100644 (file)
index 9426048..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{20 - ICITTE : 8} * 10
----
-14 13 12 11 10 0f 0e 0d 0c 0b
diff --git a/tests/pass-readme-learn-fl-num-1.nt b/tests/pass-readme-learn-fl-num-1.nt
new file mode 100644 (file)
index 0000000..63af0d7
--- /dev/null
@@ -0,0 +1,4 @@
+{le} {345:16}
+{be} {-0xabcd:32}
+---
+59 01 ff ff 54 33
diff --git a/tests/pass-readme-learn-fl-num-2.nt b/tests/pass-readme-learn-fl-num-2.nt
new file mode 100644 (file)
index 0000000..472c545
--- /dev/null
@@ -0,0 +1,11 @@
+{be}
+
+# String length in bits
+{8 * (str_end - str_beg) : 16}
+
+# String
+<str_beg>
+  "hello world!"
+<str_end>
+---
+00 60 68 65 6c 6c 6f 20 77 6f 72 6c 64 21
diff --git a/tests/pass-readme-learn-fl-num-3.nt b/tests/pass-readme-learn-fl-num-3.nt
new file mode 100644 (file)
index 0000000..9426048
--- /dev/null
@@ -0,0 +1,3 @@
+{20 - ICITTE : 8} * 10
+---
+14 13 12 11 10 0f 0e 0d 0c 0b
diff --git a/tests/pass-readme-learn-fl-num-4.nt b/tests/pass-readme-learn-fl-num-4.nt
new file mode 100644 (file)
index 0000000..6998c8f
--- /dev/null
@@ -0,0 +1,4 @@
+{le}
+{2 * 0.0529 : 32}
+---
+ac ad d8 3d
This page took 0.042357 seconds and 4 git commands to generate.