Allow constant floating point numbers as macro expansion parameter v0.17.0
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sun, 8 Oct 2023 20:41:11 +0000 (16:41 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 10 Oct 2023 23:26:28 +0000 (19:26 -0400)
Change-Id: I695ff0a5b41cf61f20b0ac237149bc4801e23fea
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Reviewed-on: https://review.lttng.org/c/normand/+/11017
Tested-by: jenkins <jenkins@lttng.org>
README.adoc
normand/normand.py
pyproject.toml
tests/fail-macro-exp-inval-param-float.nt [deleted file]
tests/fail-macro-exp-inval-param-str.nt
tests/pass-macro-exp-param-float.nt [new file with mode: 0644]
tests/pass-no-ws.nt
tests/pass-readme-learn-macro-exp-3.nt [new file with mode: 0644]

index e2c9154f8e2acceb0b568fba30daaf9adb5a7001..522695d058813710bd958516a66de5e262403e18 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.16, meaning both the Normand
+WARNING: This version of Normand is 0.17, meaning both the Normand
 language and the module/CLI interface aren't stable.
 
 ifdef::env-github[]
@@ -1700,6 +1700,8 @@ A parameter value is one of:
 --
 * A <<const-int,constant integer>>, possibly negative.
 
+* A constant floating point number.
+
 * The ``pass:[{]`` prefix, a valid {py3} expression of which the
   evaluation result type is `int` or `bool` (automatically converted to
   `int`), and the ``pass:[}]`` suffix.
@@ -1792,6 +1794,24 @@ Output:
 ----
 ====
 
+====
+Input:
+
+----
+!macro flt32be(val) {be} {val : 32} !end
+
+"CHEETOS"
+m:flt32be(-42.17)
+m:flt32be(56.23e-4)
+----
+
+Output:
+
+----
+43 48 45 45 54 4f 53 c2  28 ae 14 3b b8 41 25     ┆ CHEETOS•(••;•A%
+----
+====
+
 === Post-item repetition
 
 A _post-item repetition_ represents the bytes of an item repeated a
index 2a2acfae341e2b82392d9a2195c580d524b6f34f..790583080256fe048fd7f6b4fc0d75120d66c8b2 100644 (file)
@@ -30,7 +30,7 @@
 # Upstream repository: <https://github.com/efficios/normand>.
 
 __author__ = "Philippe Proulx"
-__version__ = "0.16.0"
+__version__ = "0.17.0"
 __all__ = [
     "__author__",
     "__version__",
@@ -1106,7 +1106,7 @@ class _Parser:
 
     # Common constant integer patterns
     _pos_const_int_pat = re.compile(
-        r"0[Xx][A-Fa-f0-9]+|0[Oo][0-7]+|0[Bb][01]+|[A-Fa-f0-9]+[hH]|[0-7]+[qQoO]|[01]+[bB]|\d+"
+        r"(?:0[Xx][A-Fa-f0-9]+|0[Oo][0-7]+|0[Bb][01]+|[A-Fa-f0-9]+[hH]|[0-7]+[qQoO]|[01]+[bB]|\d+)\b"
     )
     _const_int_pat = re.compile(r"(?P<neg>-)?(?:{})".format(_pos_const_int_pat.pattern))
 
@@ -1254,6 +1254,99 @@ class _Parser:
         # Return item
         return _AlignOffset(val, pad_val, begin_text_loc)
 
+    # Patterns for _expect_expr()
+    _inner_expr_prefix_pat = re.compile(r"\{")
+    _inner_expr_pat = re.compile(r"[^}]+")
+    _inner_expr_suffix_pat = re.compile(r"\}")
+    _const_float_pat = re.compile(
+        r"[-+]?(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[Ee][+-]?\d+)?\b"
+    )
+
+    # Parses an expression outside a `{`/`}` context.
+    #
+    # This function accepts:
+    #
+    # • A Python expression within `{` and `}`.
+    #
+    # • A Python name.
+    #
+    # • If `accept_const_int` is `True`: a constant integer, which may
+    #   be negative if `allow_neg_int` is `True`.
+    #
+    # • If `accept_float` is `True`: a constant floating point number.
+    #
+    # Returns the stripped expression string and AST expression.
+    def _expect_expr(
+        self,
+        accept_const_int: bool = False,
+        allow_neg_int: bool = False,
+        accept_const_float: bool = False,
+    ):
+        begin_text_loc = self._text_loc
+
+        # Constant floating point number?
+        m = None
+
+        if accept_const_float:
+            m = self._try_parse_pat(self._const_float_pat)
+
+            if m is not None:
+                return self._ast_expr_from_str(m.group(0), begin_text_loc)
+
+        # Constant integer?
+        m = None
+
+        if accept_const_int:
+            m = self._try_parse_pat(self._const_int_pat)
+
+            if m is not None:
+                # Negative and allowed?
+                if m.group("neg") == "-" and not allow_neg_int:
+                    _raise_error(
+                        "Expecting a positive constant integer", begin_text_loc
+                    )
+
+                expr_str = self._norm_const_int(m.group(0))
+                return self._ast_expr_from_str(expr_str, begin_text_loc)
+
+        # Name?
+        m = self._try_parse_pat(_py_name_pat)
+
+        if m is not None:
+            return self._ast_expr_from_str(m.group(0), begin_text_loc)
+
+        # Expect `{`
+        msg_accepted_parts = ["a name", "or `{`"]
+
+        if accept_const_float:
+            msg_accepted_parts.insert(0, "a constant floating point number")
+
+        if accept_const_int:
+            msg_pos = "" if allow_neg_int else "positive "
+            msg_accepted_parts.insert(0, "a {}constant integer".format(msg_pos))
+
+        if len(msg_accepted_parts) == 2:
+            msg_accepted = " ".join(msg_accepted_parts)
+        else:
+            msg_accepted = ", ".join(msg_accepted_parts)
+
+        self._expect_pat(
+            self._inner_expr_prefix_pat,
+            "Expecting {}".format(msg_accepted),
+        )
+
+        # Expect an expression
+        self._skip_ws()
+        expr_text_loc = self._text_loc
+        m = self._expect_pat(self._inner_expr_pat, "Expecting an expression")
+        expr_str = m.group(0)
+
+        # Expect `}`
+        self._skip_ws()
+        self._expect_pat(self._inner_expr_suffix_pat, "Expecting `}`")
+
+        return self._ast_expr_from_str(expr_str, expr_text_loc)
+
     # Patterns for _try_parse_fill_until()
     _fill_until_prefix_pat = re.compile(r"\+")
     _fill_until_pad_val_prefix_pat = re.compile(r"~")
@@ -1269,7 +1362,7 @@ class _Parser:
 
         # Expect expression
         self._skip_ws()
-        expr_str, expr = self._expect_const_int_name_expr(True)
+        expr_str, expr = self._expect_expr(accept_const_int=True)
 
         # Padding value
         pad_val = self._parse_pad_val()
@@ -1277,67 +1370,10 @@ class _Parser:
         # Return item
         return _FillUntil(expr_str, expr, pad_val, begin_text_loc)
 
-    # Patterns for _expect_rep_mul_expr()
-    _inner_expr_prefix_pat = re.compile(r"\{")
-    _inner_expr_pat = re.compile(r"[^}]+")
-    _inner_expr_suffix_pat = re.compile(r"\}")
-
-    # Parses a constant integer if `accept_const_int` is `True`
-    # (possibly negative if `allow_neg` is `True`), a name, or an
-    # expression within `{` and `}`.
-    def _expect_const_int_name_expr(
-        self, accept_const_int: bool, allow_neg: bool = False
-    ):
-        expr_text_loc = self._text_loc
-
-        # Constant integer?
-        m = None
-
-        if accept_const_int:
-            m = self._try_parse_pat(self._const_int_pat)
-
-        if m is None:
-            # Name?
-            m = self._try_parse_pat(_py_name_pat)
-
-            if m is None:
-                # Expression?
-                if self._try_parse_pat(self._inner_expr_prefix_pat) is None:
-                    pos_msg = "" if allow_neg else "positive "
-
-                    if accept_const_int:
-                        mid_msg = "a {}constant integer, a name, or `{{`".format(
-                            pos_msg
-                        )
-                    else:
-                        mid_msg = "a name or `{`"
-
-                    # At this point it's invalid
-                    self._raise_error("Expecting {}".format(mid_msg))
-
-                # Expect an expression
-                self._skip_ws()
-                expr_text_loc = self._text_loc
-                m = self._expect_pat(self._inner_expr_pat, "Expecting an expression")
-                expr_str = m.group(0)
-
-                # Expect `}`
-                self._skip_ws()
-                self._expect_pat(self._inner_expr_suffix_pat, "Expecting `}`")
-            else:
-                expr_str = m.group(0)
-        else:
-            if m.group("neg") == "-" and not allow_neg:
-                _raise_error("Expecting a positive constant integer", expr_text_loc)
-
-            expr_str = self._norm_const_int(m.group(0))
-
-        return self._ast_expr_from_str(expr_str, expr_text_loc)
-
     # Parses the multiplier expression of a repetition (block or
     # post-item) and returns the expression string and AST node.
     def _expect_rep_mul_expr(self):
-        return self._expect_const_int_name_expr(True)
+        return self._expect_expr(accept_const_int=True)
 
     # Common block end pattern
     _block_end_pat = re.compile(r"!end\b")
@@ -1389,7 +1425,7 @@ class _Parser:
 
         # Expect expression
         self._skip_ws_and_comments()
-        expr_str, expr = self._expect_const_int_name_expr(False)
+        expr_str, expr = self._expect_expr()
 
         # Parse "true" items
         self._skip_ws_and_comments()
@@ -1564,7 +1600,11 @@ class _Parser:
             param_text_loc = self._text_loc
             params.append(
                 _MacroExpParam(
-                    *self._expect_const_int_name_expr(True, True),
+                    *self._expect_expr(
+                        accept_const_int=True,
+                        allow_neg_int=True,
+                        accept_const_float=True,
+                    ),
                     text_loc=param_text_loc
                 )
             )
index 590e9c43a519db105b33068eb568fa80de2bfc0a..654adfc34222bc74503fc8482d946a973758d90e 100644 (file)
@@ -23,7 +23,7 @@
 
 [tool.poetry]
 name = 'normand'
-version = '0.16.0'
+version = '0.17.0'
 description = 'Text-to-binary processor with its own language'
 license = 'MIT'
 authors = ['Philippe Proulx <eeppeliteloop@gmail.com>']
diff --git a/tests/fail-macro-exp-inval-param-float.nt b/tests/fail-macro-exp-inval-param-float.nt
deleted file mode 100644 (file)
index 80f4db2..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-!macro salut(a, b, c)
-  "meow" 23 42
-!end
-
-aa bb m:salut(42, 45.12, 12) ff
----
-5:21 - Expecting `,`
index abfc58dfa8cf2bfbdae1b27a997a5134bbe28130..4fb486a821baee6ac0f3483239027c0061c716f3 100644 (file)
@@ -4,4 +4,4 @@
 
 aa bb m:salut(42, "yo", 12) ff
 ---
-5:19 - Expecting a constant integer, a name, or `{`
+5:19 - Expecting a constant integer, a constant floating point number, a name, or `{`
diff --git a/tests/pass-macro-exp-param-float.nt b/tests/pass-macro-exp-param-float.nt
new file mode 100644 (file)
index 0000000..f06de84
--- /dev/null
@@ -0,0 +1,19 @@
+!macro salut(a, b, c)
+  "meow" {b : 32}
+!end
+
+{be}
+aa bb
+m:salut(42, 45.12, 12)
+m:salut(42, -12.34e9, 12)
+ff
+---
+aa bb
+
+6d 65 6f 77
+42 34 7a e1
+
+6d 65 6f 77
+d0 37 e1 5d
+
+ff
index d5161d168d6abba4d9afc590f77c636b25dd72b5..729acd66443766c51c888d7affd9b47eea6a81ba 100644 (file)
@@ -1,4 +1,4 @@
-abcdef$-92%1100101010"meow"89u16be"mix"*2(45$45)*3ff
+abcdef$-92%1100101010"meow"89u16be"mix"*2(45$45)*3/ff
 ---
 ab cd ef a4 ca 10 6d 65 6f 77 89 00 6d 00 69 00
 78 00 6d 00 69 00 78 45 2d 45 2d 45 2d ff
diff --git a/tests/pass-readme-learn-macro-exp-3.nt b/tests/pass-readme-learn-macro-exp-3.nt
new file mode 100644 (file)
index 0000000..865c682
--- /dev/null
@@ -0,0 +1,7 @@
+!macro flt32be(val) {be} {val : 32} !end
+
+"CHEETOS"
+m:flt32be(-42.17)
+m:flt32be(56.23e-4)
+---
+43 48 45 45 54 4f 53 c2 28 ae 14 3b b8 41 25
This page took 0.030467 seconds and 4 git commands to generate.