From: Philippe Proulx Date: Sun, 8 Oct 2023 20:41:11 +0000 (-0400) Subject: Allow constant floating point numbers as macro expansion parameter X-Git-Tag: v0.17.0 X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=dbd84e7433af051baa4629d4564aa5ef078b6934;p=normand.git Allow constant floating point numbers as macro expansion parameter Change-Id: I695ff0a5b41cf61f20b0ac237149bc4801e23fea Signed-off-by: Philippe Proulx Reviewed-on: https://review.lttng.org/c/normand/+/11017 Tested-by: jenkins --- diff --git a/README.adoc b/README.adoc index e2c9154..522695d 100644 --- a/README.adoc +++ b/README.adoc @@ -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 <>, 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 diff --git a/normand/normand.py b/normand/normand.py index 2a2acfa..7905830 100644 --- a/normand/normand.py +++ b/normand/normand.py @@ -30,7 +30,7 @@ # Upstream repository: . __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-)?(?:{})".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 ) ) diff --git a/pyproject.toml b/pyproject.toml index 590e9c4..654adfc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 '] diff --git a/tests/fail-macro-exp-inval-param-float.nt b/tests/fail-macro-exp-inval-param-float.nt deleted file mode 100644 index 80f4db2..0000000 --- a/tests/fail-macro-exp-inval-param-float.nt +++ /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 `,` diff --git a/tests/fail-macro-exp-inval-param-str.nt b/tests/fail-macro-exp-inval-param-str.nt index abfc58d..4fb486a 100644 --- a/tests/fail-macro-exp-inval-param-str.nt +++ b/tests/fail-macro-exp-inval-param-str.nt @@ -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 index 0000000..f06de84 --- /dev/null +++ b/tests/pass-macro-exp-param-float.nt @@ -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 diff --git a/tests/pass-no-ws.nt b/tests/pass-no-ws.nt index d5161d1..729acd6 100644 --- a/tests/pass-no-ws.nt +++ b/tests/pass-no-ws.nt @@ -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 index 0000000..865c682 --- /dev/null +++ b/tests/pass-readme-learn-macro-exp-3.nt @@ -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