dc: tweak parsing
authorBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Mon, 16 Feb 2015 16:12:04 +0000 (17:12 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 16 Feb 2015 16:12:04 +0000 (17:12 +0100)
https://bugs.debian.org/538685
dc requires whitespace between language elements.

We were requiring
1 2 + p
instead of the abbreviated
1 2+p
(for example).

function                                             old     new   delta
stack_machine                                         97     126     +29
dc_main                                              117      79     -38
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 29/-38)             Total: -9 bytes

Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
miscutils/dc.c
testsuite/dc.tests [new file with mode: 0755]

index 6bcfbe2493a372ff3555e40c173af843567b3ee3..a7bd360d223eedbee91fe38228b93e93280ed4b3 100644 (file)
@@ -196,14 +196,6 @@ struct op {
 };
 
 static const struct op operators[] = {
-       {"+",   add},
-       {"add", add},
-       {"-",   sub},
-       {"sub", sub},
-       {"*",   mul},
-       {"mul", mul},
-       {"/",   divide},
-       {"div", divide},
 #if ENABLE_FEATURE_DC_LIBM
        {"**",  power},
        {"exp", power},
@@ -216,28 +208,47 @@ static const struct op operators[] = {
        {"not", not},
        {"eor", eor},
        {"xor", eor},
+       {"+",   add},
+       {"add", add},
+       {"-",   sub},
+       {"sub", sub},
+       {"*",   mul},
+       {"mul", mul},
+       {"/",   divide},
+       {"div", divide},
        {"p", print_no_pop},
        {"f", print_stack_no_pop},
        {"o", set_output_base},
 };
 
+/* Feed the stack machine */
 static void stack_machine(const char *argument)
 {
        char *end;
-       double d;
+       double number;
        const struct op *o;
 
-       d = strtod(argument, &end);
-       if (end != argument && *end == '\0') {
-               push(d);
-               return;
+ next:
+       number = strtod(argument, &end);
+       if (end != argument) {
+               argument = end;
+               push(number);
+               goto next;
        }
 
+       /* We might have matched a digit, eventually advance the argument */
+       argument = skip_whitespace(argument);
+
+       if (*argument == '\0')
+               return;
+
        o = operators;
        do {
-               if (strcmp(o->name, argument) == 0) {
+               const size_t name_len = strlen(o->name);
+               if (strncmp(o->name, argument, name_len) == 0) {
+                       argument += name_len;
                        o->function();
-                       return;
+                       goto next;
                }
                o++;
        } while (o != operators + ARRAY_SIZE(operators));
@@ -254,25 +265,11 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
        if (!argv[0]) {
                /* take stuff from stdin if no args are given */
                char *line;
-               char *cursor;
-               char *token;
                while ((line = xmalloc_fgetline(stdin)) != NULL) {
-                       cursor = line;
-                       while (1) {
-                               token = skip_whitespace(cursor);
-                               if (*token == '\0')
-                                       break;
-                               cursor = skip_non_whitespace(token);
-                               if (*cursor != '\0')
-                                       *cursor++ = '\0';
-                               stack_machine(token);
-                       }
+                       stack_machine(line);
                        free(line);
                }
        } else {
-               // why? it breaks "dc -2 2 + p"
-               //if (argv[0][0] == '-')
-               //      bb_show_usage();
                do {
                        stack_machine(*argv);
                } while (*++argv);
diff --git a/testsuite/dc.tests b/testsuite/dc.tests
new file mode 100755 (executable)
index 0000000..a5da537
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Copyright 2015 by Bernhard Reutner-Fischer
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+testing "dc basic syntax (stdin, multiple args)" \
+       "dc" \
+       "30\n" \
+       "" "10 20+p"
+
+testing "dc basic syntax (argv, single arg)" \
+       "dc '10 20+p'" \
+       "30\n" \
+       "" ""
+
+testing "dc basic syntax (argv, multiple args)" \
+       "dc 10 20+p" \
+       "30\n" \
+       "" ""
+
+testing "dc complex with spaces (single arg)" \
+       "dc '8 8 * 2 2 + / p'" \
+       "16\n" \
+       "" ""
+
+testing "dc complex without spaces (single arg)" \
+       "dc '8 8*2 2+/p'" \
+       "16\n" \
+       "" ""
+
+testing "dc complex with spaces (multiple args)" \
+       "dc 8 8 \* 2 2 + / p" \
+       "16\n" \
+       "" ""
+
+testing "dc complex without spaces (multiple args)" \
+       "dc 8 8\*2 2+/p" \
+       "16\n" \
+       "" ""
+
+exit $FAILCOUNT
+
+# we do not support arguments
+testing "dc -e <exprs>" \
+       "dc -e '10 2+f'" \
+       "12\n" \
+       "" ""
+
+testing "dc -f <exprs-from-given-file>" \
+       "dc -f input" \
+       "12\n" \
+       "10 2+f" ""
+