kconfig: allow use of relations other than (in)equality
[carl9170fw.git] / config / expr.c
index 0d7364ce9132f2c67f489203867b9d623c1148e2..667d1aa237114453c28bafac618e9552405a19c4 100644 (file)
@@ -79,6 +79,10 @@ struct expr *expr_copy(const struct expr *org)
                e->left.expr = expr_copy(org->left.expr);
                break;
        case E_EQUAL:
+       case E_GEQ:
+       case E_GTH:
+       case E_LEQ:
+       case E_LTH:
        case E_UNEQUAL:
                e->left.sym = org->left.sym;
                e->right.sym = org->right.sym;
@@ -111,6 +115,10 @@ void expr_free(struct expr *e)
                expr_free(e->left.expr);
                return;
        case E_EQUAL:
+       case E_GEQ:
+       case E_GTH:
+       case E_LEQ:
+       case E_LTH:
        case E_UNEQUAL:
                break;
        case E_OR:
@@ -197,6 +205,10 @@ static int expr_eq(struct expr *e1, struct expr *e2)
                return 0;
        switch (e1->type) {
        case E_EQUAL:
+       case E_GEQ:
+       case E_GTH:
+       case E_LEQ:
+       case E_LTH:
        case E_UNEQUAL:
                return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
        case E_SYMBOL:
@@ -587,6 +599,10 @@ struct expr *expr_transform(struct expr *e)
                return NULL;
        switch (e->type) {
        case E_EQUAL:
+       case E_GEQ:
+       case E_GTH:
+       case E_LEQ:
+       case E_LTH:
        case E_UNEQUAL:
        case E_SYMBOL:
        case E_LIST:
@@ -659,6 +675,22 @@ struct expr *expr_transform(struct expr *e)
                        e = tmp;
                        e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
                        break;
+               case E_LEQ:
+               case E_GEQ:
+                       // !a<='x' -> a>'x'
+                       tmp = e->left.expr;
+                       free(e);
+                       e = tmp;
+                       e->type = e->type == E_LEQ ? E_GTH : E_LTH;
+                       break;
+               case E_LTH:
+               case E_GTH:
+                       // !a<'x' -> a>='x'
+                       tmp = e->left.expr;
+                       free(e);
+                       e = tmp;
+                       e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
+                       break;
                case E_OR:
                        // !(a || b) -> !a && !b
                        tmp = e->left.expr;
@@ -729,6 +761,10 @@ int expr_contains_symbol(struct expr *dep, struct symbol *sym)
        case E_SYMBOL:
                return dep->left.sym == sym;
        case E_EQUAL:
+       case E_GEQ:
+       case E_GTH:
+       case E_LEQ:
+       case E_LTH:
        case E_UNEQUAL:
                return dep->left.sym == sym ||
                       dep->right.sym == sym;
@@ -803,6 +839,10 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
        case E_NOT:
                return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
        case E_UNEQUAL:
+       case E_LTH:
+       case E_LEQ:
+       case E_GTH:
+       case E_GEQ:
        case E_EQUAL:
                if (type == E_EQUAL) {
                        if (sym == &symbol_yes)
@@ -830,10 +870,57 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
        return NULL;
 }
 
+enum string_value_kind {
+       k_string,
+       k_signed,
+       k_unsigned,
+       k_invalid
+};
+
+union string_value {
+       unsigned long long u;
+       signed long long s;
+};
+
+static enum string_value_kind expr_parse_string(const char *str,
+                                               enum symbol_type type,
+                                               union string_value *val)
+{
+       char *tail;
+       enum string_value_kind kind;
+
+       errno = 0;
+       switch (type) {
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               return k_string;
+       case S_INT:
+               val->s = strtoll(str, &tail, 10);
+               kind = k_signed;
+               break;
+       case S_HEX:
+               val->u = strtoull(str, &tail, 16);
+               kind = k_unsigned;
+               break;
+       case S_STRING:
+       case S_UNKNOWN:
+               val->s = strtoll(str, &tail, 0);
+               kind = k_signed;
+               break;
+       default:
+               return k_invalid;
+       }
+       return !errno && !*tail && tail > str && isxdigit(tail[-1])
+              ? kind : k_string;
+}
+
 tristate expr_calc_value(struct expr *e)
 {
        tristate val1, val2;
        const char *str1, *str2;
+       enum string_value_kind k1 = k_string, k2 = k_string;
+       union string_value lval = {}, rval = {};
+       int res;
 
        if (!e)
                return yes;
@@ -854,21 +941,57 @@ tristate expr_calc_value(struct expr *e)
                val1 = expr_calc_value(e->left.expr);
                return EXPR_NOT(val1);
        case E_EQUAL:
-               sym_calc_value(e->left.sym);
-               sym_calc_value(e->right.sym);
-               str1 = sym_get_string_value(e->left.sym);
-               str2 = sym_get_string_value(e->right.sym);
-               return !strcmp(str1, str2) ? yes : no;
+       case E_GEQ:
+       case E_GTH:
+       case E_LEQ:
+       case E_LTH:
        case E_UNEQUAL:
-               sym_calc_value(e->left.sym);
-               sym_calc_value(e->right.sym);
-               str1 = sym_get_string_value(e->left.sym);
-               str2 = sym_get_string_value(e->right.sym);
-               return !strcmp(str1, str2) ? no : yes;
+               break;
        default:
                printf("expr_calc_value: %d?\n", e->type);
                return no;
        }
+
+       sym_calc_value(e->left.sym);
+       sym_calc_value(e->right.sym);
+       str1 = sym_get_string_value(e->left.sym);
+       str2 = sym_get_string_value(e->right.sym);
+
+       if (e->left.sym->type != S_STRING || e->right.sym->type != S_STRING) {
+               k1 = expr_parse_string(str1, e->left.sym->type, &lval);
+               k2 = expr_parse_string(str2, e->right.sym->type, &rval);
+       }
+
+       if (k1 == k_string || k2 == k_string)
+               res = strcmp(str1, str2);
+       else if (k1 == k_invalid || k2 == k_invalid) {
+               if (e->type != E_EQUAL && e->type != E_UNEQUAL) {
+                       printf("Cannot compare \"%s\" and \"%s\"\n", str1, str2);
+                       return no;
+               }
+               res = strcmp(str1, str2);
+       } else if (k1 == k_unsigned || k2 == k_unsigned)
+               res = (lval.u > rval.u) - (lval.u < rval.u);
+       else /* if (k1 == k_signed && k2 == k_signed) */
+               res = (lval.s > rval.s) - (lval.s < rval.s);
+
+       switch(e->type) {
+       case E_EQUAL:
+               return res ? no : yes;
+       case E_GEQ:
+               return res >= 0 ? yes : no;
+       case E_GTH:
+               return res > 0 ? yes : no;
+       case E_LEQ:
+               return res <= 0 ? yes : no;
+       case E_LTH:
+               return res < 0 ? yes : no;
+       case E_UNEQUAL:
+               return res ? yes : no;
+       default:
+               printf("expr_calc_value: relation %d?\n", e->type);
+               return no;
+       }
 }
 
 static int expr_compare_type(enum expr_type t1, enum expr_type t2)
@@ -876,6 +999,12 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2)
        if (t1 == t2)
                return 0;
        switch (t1) {
+       case E_LEQ:
+       case E_LTH:
+       case E_GEQ:
+       case E_GTH:
+               if (t2 == E_EQUAL || t2 == E_UNEQUAL)
+                       return 1;
        case E_EQUAL:
        case E_UNEQUAL:
                if (t2 == E_NOT)
@@ -969,6 +1098,24 @@ void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *
                fn(data, NULL, "=");
                fn(data, e->right.sym, e->right.sym->name);
                break;
+       case E_LEQ:
+       case E_LTH:
+               if (e->left.sym->name)
+                       fn(data, e->left.sym, e->left.sym->name);
+               else
+                       fn(data, NULL, "<choice>");
+               fn(data, NULL, e->type == E_LEQ ? "<=" : "<");
+               fn(data, e->right.sym, e->right.sym->name);
+               break;
+       case E_GEQ:
+       case E_GTH:
+               if (e->left.sym->name)
+                       fn(data, e->left.sym, e->left.sym->name);
+               else
+                       fn(data, NULL, "<choice>");
+               fn(data, NULL, e->type == E_LEQ ? ">=" : ">");
+               fn(data, e->right.sym, e->right.sym->name);
+               break;
        case E_UNEQUAL:
                if (e->left.sym->name)
                        fn(data, e->left.sym, e->left.sym->name);