kconfig: allow use of relations other than (in)equality

Over the years I found it desirable to be able to use all sorts of
relations, not just (in)equality. And apparently I'm not the only one,
as there's at least one example in the tree where the programmer
assumed this would work (see DEBUG_UART_8250_WORD in
arch/arm/Kconfig.debug). Another possible use would e.g. be to fold the
two SMP/NR_CPUS prompts into one: SMP could be promptless, simply
depending on NR_CPUS > 1.

A (desirable) side effect of this change - resulting from numeric
values now necessarily being compared as numbers rather than as
strings - is that comparing hex values now works as expected: Other
than int ones (which aren't allowed to have leading zeroes), zeroes
following the 0x prefix made them compare unequal even if their values
were equal.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Michal Marek <mmarek@suse.cz>
This commit is contained in:
Jan Beulich 2015-06-15 13:00:21 +01:00 committed by Michal Marek
parent 2e0d737fc7
commit 31847b67be
5 changed files with 177 additions and 11 deletions

View File

@ -79,6 +79,10 @@ struct expr *expr_copy(const struct expr *org)
e->left.expr = expr_copy(org->left.expr); e->left.expr = expr_copy(org->left.expr);
break; break;
case E_EQUAL: case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL: case E_UNEQUAL:
e->left.sym = org->left.sym; e->left.sym = org->left.sym;
e->right.sym = org->right.sym; e->right.sym = org->right.sym;
@ -111,6 +115,10 @@ void expr_free(struct expr *e)
expr_free(e->left.expr); expr_free(e->left.expr);
return; return;
case E_EQUAL: case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL: case E_UNEQUAL:
break; break;
case E_OR: case E_OR:
@ -197,6 +205,10 @@ static int expr_eq(struct expr *e1, struct expr *e2)
return 0; return 0;
switch (e1->type) { switch (e1->type) {
case E_EQUAL: case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL: case E_UNEQUAL:
return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
case E_SYMBOL: case E_SYMBOL:
@ -587,6 +599,10 @@ struct expr *expr_transform(struct expr *e)
return NULL; return NULL;
switch (e->type) { switch (e->type) {
case E_EQUAL: case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL: case E_UNEQUAL:
case E_SYMBOL: case E_SYMBOL:
case E_LIST: case E_LIST:
@ -659,6 +675,22 @@ struct expr *expr_transform(struct expr *e)
e = tmp; e = tmp;
e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
break; 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: case E_OR:
// !(a || b) -> !a && !b // !(a || b) -> !a && !b
tmp = e->left.expr; tmp = e->left.expr;
@ -729,6 +761,10 @@ int expr_contains_symbol(struct expr *dep, struct symbol *sym)
case E_SYMBOL: case E_SYMBOL:
return dep->left.sym == sym; return dep->left.sym == sym;
case E_EQUAL: case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL: case E_UNEQUAL:
return dep->left.sym == sym || return dep->left.sym == sym ||
dep->right.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: case E_NOT:
return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
case E_UNEQUAL: case E_UNEQUAL:
case E_LTH:
case E_LEQ:
case E_GTH:
case E_GEQ:
case E_EQUAL: case E_EQUAL:
if (type == E_EQUAL) { if (type == E_EQUAL) {
if (sym == &symbol_yes) if (sym == &symbol_yes)
@ -830,10 +870,57 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
return NULL; 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 expr_calc_value(struct expr *e)
{ {
tristate val1, val2; tristate val1, val2;
const char *str1, *str2; const char *str1, *str2;
enum string_value_kind k1 = k_string, k2 = k_string;
union string_value lval = {}, rval = {};
int res;
if (!e) if (!e)
return yes; return yes;
@ -854,21 +941,57 @@ tristate expr_calc_value(struct expr *e)
val1 = expr_calc_value(e->left.expr); val1 = expr_calc_value(e->left.expr);
return EXPR_NOT(val1); return EXPR_NOT(val1);
case E_EQUAL: case E_EQUAL:
sym_calc_value(e->left.sym); case E_GEQ:
sym_calc_value(e->right.sym); case E_GTH:
str1 = sym_get_string_value(e->left.sym); case E_LEQ:
str2 = sym_get_string_value(e->right.sym); case E_LTH:
return !strcmp(str1, str2) ? yes : no;
case E_UNEQUAL: case E_UNEQUAL:
sym_calc_value(e->left.sym); break;
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;
default: default:
printf("expr_calc_value: %d?\n", e->type); printf("expr_calc_value: %d?\n", e->type);
return no; 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) 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) if (t1 == t2)
return 0; return 0;
switch (t1) { 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_EQUAL:
case E_UNEQUAL: case E_UNEQUAL:
if (t2 == E_NOT) 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, NULL, "=");
fn(data, e->right.sym, e->right.sym->name); fn(data, e->right.sym, e->right.sym->name);
break; 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: case E_UNEQUAL:
if (e->left.sym->name) if (e->left.sym->name)
fn(data, e->left.sym, e->left.sym->name); fn(data, e->left.sym, e->left.sym->name);

View File

@ -29,7 +29,9 @@ typedef enum tristate {
} tristate; } tristate;
enum expr_type { enum expr_type {
E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LIST, E_SYMBOL, E_RANGE E_NONE, E_OR, E_AND, E_NOT,
E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ,
E_LIST, E_SYMBOL, E_RANGE
}; };
union expr_data { union expr_data {

View File

@ -1166,6 +1166,10 @@ static struct symbol *sym_check_expr_deps(struct expr *e)
case E_NOT: case E_NOT:
return sym_check_expr_deps(e->left.expr); return sym_check_expr_deps(e->left.expr);
case E_EQUAL: case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL: case E_UNEQUAL:
sym = sym_check_deps(e->left.sym); sym = sym_check_deps(e->left.sym);
if (sym) if (sym)

View File

@ -122,6 +122,10 @@ n [A-Za-z0-9_]
"!" return T_NOT; "!" return T_NOT;
"=" return T_EQUAL; "=" return T_EQUAL;
"!=" return T_UNEQUAL; "!=" return T_UNEQUAL;
"<=" return T_LESS_EQUAL;
">=" return T_GREATER_EQUAL;
"<" return T_LESS;
">" return T_GREATER;
\"|\' { \"|\' {
str = yytext[0]; str = yytext[0];
new_string(); new_string();

View File

@ -69,6 +69,10 @@ static struct menu *current_menu, *current_entry;
%token <string> T_WORD %token <string> T_WORD
%token <string> T_WORD_QUOTE %token <string> T_WORD_QUOTE
%token T_UNEQUAL %token T_UNEQUAL
%token T_LESS
%token T_LESS_EQUAL
%token T_GREATER
%token T_GREATER_EQUAL
%token T_CLOSE_PAREN %token T_CLOSE_PAREN
%token T_OPEN_PAREN %token T_OPEN_PAREN
%token T_EOL %token T_EOL
@ -76,6 +80,7 @@ static struct menu *current_menu, *current_entry;
%left T_OR %left T_OR
%left T_AND %left T_AND
%left T_EQUAL T_UNEQUAL %left T_EQUAL T_UNEQUAL
%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL
%nonassoc T_NOT %nonassoc T_NOT
%type <string> prompt %type <string> prompt
@ -467,6 +472,10 @@ if_expr: /* empty */ { $$ = NULL; }
; ;
expr: symbol { $$ = expr_alloc_symbol($1); } expr: symbol { $$ = expr_alloc_symbol($1); }
| symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); }
| symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); }
| symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); }
| symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); }
| symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
| symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
| T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; }