bug-make
[Top][All Lists]

## [PATCH 2/2] compare function

 From: Jouke Witteveen Subject: [PATCH 2/2] compare function Date: Mon, 8 Jun 2020 22:20:47 +0200

```This is an implementation of a \$(compare) function as proposed by
Edward Welbourne. It differs from his original proposal in that it behaves
differently when given 4 or 5 arguments. In short, it's signature is

\$(compare lhs,rhs,less-than[,greater-or-equal])
\$(compare lhs,rhs,less-than,equal,greater-than)

Documentation and a proper commit message is still missing, but this
proof-of-concept shows that it is in fact pretty simple to implement.

Additionally, I feel that the interface is clean. In that way, it differs
from the various proposals for integer operations. After thinking about
them some more, I came to dislike all current proposals because of the
unintuitive behavior of subtraction and division. We should only support
integer mathematics, so division is always going to be integer division,
which suggests that we need a modulus operator as well. Moreover, 1/\$x
will not be supported, so we can't implement the same behavior for
\$(math -,\$x) as for \$(math /,\$x). While verbose, the only clean way I can
think of is simply \$(add \$x,\$y), \$(subtract \$x,\$y), etc. All supporting
precisely two arguments. Multi argument versions can be defined like

sum = \$(let first rest,\$1, \
\$(if \$(rest),\$(add \$(first),\$(call sum,\$(rest))), \
\$(first)))

I'd like to repeat that on POSIX systems, mathematical expressions can be
evaluated using

expr = \$(shell expr '\$1')

Indeed, not all environments are POSIX, but this does show that \$(compare)
on its own (without any mathematical operators in make) can be very useful.
---
src/function.c                  | 47 +++++++++++++++++++++++++++
tests/scripts/functions/compare | 57 +++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+)
create mode 100644 tests/scripts/functions/compare

diff --git a/src/function.c b/src/function.c
--- a/src/function.c
+++ b/src/function.c
@@ -1220,6 +1220,52 @@ func_sort (char *o, char **argv, const char *funcname
UNUSED)
return o;
}

+/*
+  \$(compare lhs,rhs,lt-part[,eq-part[,gt-part]])
+
+  LT-PART is evaluated when LHS is strictly less than RHS. Otherwise, if
+  GT-PART is not provided, EQ-PART is evaluated (if it exists). In case
+  both EQ-PART and GT-PART are provided, EQ-PART is evaluated when LHS equals
+  RHS, and GT-PART is evaluated when LHS is strictly greater than RHS.
+
+  Only one of the PARTs is evaluated, so \$(compare ...) can be used to create
+  side-effects (with \$(shell ...), for example).
+*/
+
+static char *
+func_compare (char *o, char **argv, const char *funcname UNUSED)
+{
+  char *lhs_str = expand_argument (argv[0], NULL);
+  char *rhs_str = expand_argument (argv[1], NULL);
+  long lhs, rhs;
+
+  lhs = parse_numeric (lhs_str,
+                       _("non-numeric first argument to 'compare' function"));
+  rhs = parse_numeric (rhs_str,
+                       _("non-numeric second argument to 'compare' function"));
+  free (lhs_str);
+  free (rhs_str);
+
+  argv += 2;
+  if (lhs >= rhs)
+    {
+      ++argv;
+      if (lhs > rhs && *argv && *(argv + 1))
+        ++argv;
+    }
+
+  if (*argv)
+    {
+      char *expansion = expand_argument (*argv, NULL);
+
+      o = variable_buffer_output (o, expansion, strlen (expansion));
+
+      free (expansion);
+    }
+
+  return o;
+}
+
/*
\$(if condition,true-part[,false-part])

@@ -2378,6 +2424,7 @@ static struct function_table_entry function_table_init[] =
FT_ENTRY ("info",          0,  1,  1,  func_error),
FT_ENTRY ("error",         0,  1,  1,  func_error),
FT_ENTRY ("warning",       0,  1,  1,  func_error),
+  FT_ENTRY ("compare",       3,  5,  0,  func_compare),
FT_ENTRY ("if",            2,  3,  0,  func_if),
FT_ENTRY ("or",            1,  0,  0,  func_or),
FT_ENTRY ("and",           1,  0,  0,  func_and),
diff --git a/tests/scripts/functions/compare b/tests/scripts/functions/compare
new file mode 100644
index 0000000..574397f
--- /dev/null
+++ b/tests/scripts/functions/compare
@@ -0,0 +1,57 @@
+#                                                                    -*-perl-*-
+\$description = "Test the compare function.\n";
+
+\$details = "Try various uses of compare and ensure they all give the correct
+results.\n";
+
+open(MAKEFILE, "> \$makefile");
+
+print MAKEFILE <<'EOF';
+# Negative
+n = -10
+# Zero
+z = 0
+# Positive
+p = 1000000000
+
+all:
+       @echo 1_1 \$(compare \$n,\$n,\$(shell echo lt))
+       @echo 1_2 \$(compare \$n,\$z,\$(shell echo lt))
+       @echo 1_3 \$(compare \$z,\$n,\$(shell echo lt))
+       @echo 2_1 \$(compare \$n,\$p,lt,ge)
+       @echo 2_2 \$(compare \$z,\$z,lt,ge)
+       @echo 2_3 \$(compare \$p,\$n,lt,ge)
+       @echo 3_0 \$(compare \$p,\$n,lt,eq,)
+       @echo 3_1 \$(compare \$z,\$p,lt,eq,gt)
+       @echo 3_2 \$(compare \$p,\$z,lt,eq,gt)
+       @echo 3_3 \$(compare \$p,\$p,lt,eq,gt)
+EOF
+close(MAKEFILE);
+
+&run_make_with_options(\$makefile, "", &get_logfile);
+\$answer = "1_1\n1_2 lt\n1_3\n2_1 lt\n2_2 ge\n2_3 ge\n3_0\n3_1 lt\n3_2 gt\n3_3
eq\n";
+
+
+# Test error conditions
+
+run_make_test('
+compare-e1: ; @echo \$(compare 12a,1,foo)
+compare-e2: ; @echo \$(compare 0,,foo)
+compare-e3: ; @echo \$(compare -1,9999999999999999999,foo)',
+              'compare-e1',
+              "#MAKEFILE#:2: *** non-numeric first argument to 'compare'
function: '12a'.  Stop.",
+              512);
+
+run_make_test(undef,
+              'compare-e2',
+              "#MAKEFILE#:3: *** non-numeric second argument to 'compare'
function: ''.  Stop.",
+              512);
+
+run_make_test(undef,
+              'compare-e3',
+              "#MAKEFILE#:4: *** Numerical result out of range:
'9999999999999999999'.  Stop.",
+              512);
+
+
+1;
--
2.27.0

```

reply via email to