bug-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[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
index ccad300..aa23349 100644
--- 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";
+&compare_output($answer, &get_logfile(1));
+
+
+# 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

[Prev in Thread] Current Thread [Next in Thread]