Edit

kc3-lang/libxkbcommon/src/xkbcomp/expr.c

Branch :

  • Show log

    Commit

  • Author : Ran Benita
    Date : 2012-07-17 11:03:43
    Hash : 50b25a12
    Message : Use xkb_group_index_t for group variables throughout Signed-off-by: Ran Benita <ran234@gmail.com>

  • src/xkbcomp/expr.c
  • /************************************************************
     * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
     *
     * Permission to use, copy, modify, and distribute this
     * software and its documentation for any purpose and without
     * fee is hereby granted, provided that the above copyright
     * notice appear in all copies and that both that copyright
     * notice and this permission notice appear in supporting
     * documentation, and that the name of Silicon Graphics not be
     * used in advertising or publicity pertaining to distribution
     * of the software without specific prior written permission.
     * Silicon Graphics makes no representation about the suitability
     * of this software for any purpose. It is provided "as is"
     * without any express or implied warranty.
     *
     * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
     * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
     * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
     * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
     * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
     * THE USE OR PERFORMANCE OF THIS SOFTWARE.
     *
     ********************************************************/
    
    #include "expr.h"
    
    /***====================================================================***/
    
    typedef bool (*IdentLookupFunc)(struct xkb_context *ctx, const void *priv,
                                    xkb_atom_t field, unsigned type,
                                    ExprResult *val_rtrn);
    
    /***====================================================================***/
    
    const char *
    exprOpText(unsigned type)
    {
        static char buf[32];
    
        switch (type) {
        case ExprValue:
            strcpy(buf, "literal");
            break;
        case ExprIdent:
            strcpy(buf, "identifier");
            break;
        case ExprActionDecl:
            strcpy(buf, "action declaration");
            break;
        case ExprFieldRef:
            strcpy(buf, "field reference");
            break;
        case ExprArrayRef:
            strcpy(buf, "array reference");
            break;
        case ExprKeysymList:
            strcpy(buf, "list of keysyms");
            break;
        case ExprActionList:
            strcpy(buf, "list of actions");
            break;
        case OpAdd:
            strcpy(buf, "addition");
            break;
        case OpSubtract:
            strcpy(buf, "subtraction");
            break;
        case OpMultiply:
            strcpy(buf, "multiplication");
            break;
        case OpDivide:
            strcpy(buf, "division");
            break;
        case OpAssign:
            strcpy(buf, "assignment");
            break;
        case OpNot:
            strcpy(buf, "logical not");
            break;
        case OpNegate:
            strcpy(buf, "arithmetic negation");
            break;
        case OpInvert:
            strcpy(buf, "bitwise inversion");
            break;
        case OpUnaryPlus:
            strcpy(buf, "plus sign");
            break;
        default:
            snprintf(buf, sizeof(buf), "illegal(%d)", type);
            break;
        }
        return buf;
    }
    
    static const char *
    exprTypeText(unsigned type)
    {
        static char buf[20];
    
        switch (type) {
        case TypeUnknown:
            strcpy(buf, "unknown");
            break;
        case TypeBoolean:
            strcpy(buf, "boolean");
            break;
        case TypeInt:
            strcpy(buf, "int");
            break;
        case TypeString:
            strcpy(buf, "string");
            break;
        case TypeAction:
            strcpy(buf, "action");
            break;
        case TypeKeyName:
            strcpy(buf, "keyname");
            break;
        default:
            snprintf(buf, sizeof(buf), "illegal(%d)", type);
            break;
        }
        return buf;
    }
    
    int
    ExprResolveLhs(struct xkb_keymap *keymap, ExprDef *expr,
                   ExprResult *elem_rtrn, ExprResult *field_rtrn,
                   ExprDef **index_rtrn)
    {
        switch (expr->op) {
        case ExprIdent:
            elem_rtrn->str = NULL;
            field_rtrn->str = xkb_atom_strdup(keymap->ctx,
                                              expr->value.str);
            *index_rtrn = NULL;
            return true;
        case ExprFieldRef:
            elem_rtrn->str = xkb_atom_strdup(keymap->ctx,
                                             expr->value.field.element);
            field_rtrn->str = xkb_atom_strdup(keymap->ctx,
                                              expr->value.field.field);
            *index_rtrn = NULL;
            return true;
        case ExprArrayRef:
            elem_rtrn->str = xkb_atom_strdup(keymap->ctx,
                                             expr->value.array.element);
            field_rtrn->str = xkb_atom_strdup(keymap->ctx,
                                              expr->value.array.field);
            *index_rtrn = expr->value.array.entry;
            return true;
        }
        WSGO("Unexpected operator %d in ResolveLhs\n", expr->op);
        return false;
    }
    
    static bool
    SimpleLookup(struct xkb_context *ctx, const void *priv,
                 xkb_atom_t field, unsigned type, ExprResult *val_rtrn)
    {
        const LookupEntry *entry;
        const char *str;
    
        if ((priv == NULL) || (field == XKB_ATOM_NONE) || (type != TypeInt))
            return false;
    
        str = xkb_atom_text(ctx, field);
        for (entry = priv; (entry != NULL) && (entry->name != NULL); entry++) {
            if (strcasecmp(str, entry->name) == 0) {
                val_rtrn->uval = entry->result;
                return true;
            }
        }
    
        return false;
    }
    
    static const LookupEntry modIndexNames[] = {
        { "shift", ShiftMapIndex },
        { "control", ControlMapIndex },
        { "lock", LockMapIndex },
        { "mod1", Mod1MapIndex },
        { "mod2", Mod2MapIndex },
        { "mod3", Mod3MapIndex },
        { "mod4", Mod4MapIndex },
        { "mod5", Mod5MapIndex },
        { "none", XkbNoModifier },
        { NULL, 0 }
    };
    
    bool
    LookupModIndex(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
                   unsigned type, ExprResult *val_rtrn)
    {
        return SimpleLookup(ctx, modIndexNames, field, type, val_rtrn);
    }
    
    bool
    LookupModMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
                  unsigned type, ExprResult *val_rtrn)
    {
        const char *str;
        bool ret = true;
    
        if (type != TypeInt)
            return false;
        str = xkb_atom_text(ctx, field);
        if (str == NULL)
            return false;
        if (strcasecmp(str, "all") == 0)
            val_rtrn->uval = 0xff;
        else if (strcasecmp(str, "none") == 0)
            val_rtrn->uval = 0;
        else if (LookupModIndex(ctx, priv, field, type, val_rtrn))
            val_rtrn->uval = (1 << val_rtrn->uval);
        else
            ret = false;
        return ret;
    }
    
    int
    ExprResolveBoolean(struct xkb_context *ctx, ExprDef *expr,
                       ExprResult *val_rtrn)
    {
        int ok = 0;
        const char *bogus = NULL;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type != TypeBoolean) {
                ERROR
                    ("Found constant of type %s where boolean was expected\n",
                    exprTypeText(expr->type));
                return false;
            }
            val_rtrn->ival = expr->value.ival;
            return true;
    
        case ExprIdent:
            bogus = xkb_atom_text(ctx, expr->value.str);
            if (bogus) {
                if ((strcasecmp(bogus, "true") == 0) ||
                    (strcasecmp(bogus, "yes") == 0) ||
                    (strcasecmp(bogus, "on") == 0)) {
                    val_rtrn->uval = 1;
                    return true;
                }
                else if ((strcasecmp(bogus, "false") == 0) ||
                         (strcasecmp(bogus, "no") == 0) ||
                         (strcasecmp(bogus, "off") == 0)) {
                    val_rtrn->uval = 0;
                    return true;
                }
            }
            ERROR("Identifier \"%s\" of type int is unknown\n",
                  xkb_atom_text(ctx, expr->value.str));
            return false;
    
        case ExprFieldRef:
            ERROR("Default \"%s.%s\" of type boolean is unknown\n",
                  xkb_atom_text(ctx, expr->value.field.element),
                  xkb_atom_text(ctx, expr->value.field.field));
            return false;
    
        case OpInvert:
        case OpNot:
            ok = ExprResolveBoolean(ctx, expr, val_rtrn);
            if (ok)
                val_rtrn->uval = !val_rtrn->uval;
            return ok;
        case OpAdd:
            if (bogus == NULL)
                bogus = "Addition";
        case OpSubtract:
            if (bogus == NULL)
                bogus = "Subtraction";
        case OpMultiply:
            if (bogus == NULL)
                bogus = "Multiplication";
        case OpDivide:
            if (bogus == NULL)
                bogus = "Division";
        case OpAssign:
            if (bogus == NULL)
                bogus = "Assignment";
        case OpNegate:
            if (bogus == NULL)
                bogus = "Negation";
            ERROR("%s of boolean values not permitted\n", bogus);
            break;
    
        case OpUnaryPlus:
            ERROR("Unary \"+\" operator not permitted for boolean values\n");
            break;
    
        default:
            WSGO("Unknown operator %d in ResolveBoolean\n", expr->op);
            break;
        }
        return false;
    }
    
    int
    ExprResolveFloat(struct xkb_context *ctx, ExprDef *expr,
                     ExprResult *val_rtrn)
    {
        int ok = 0;
        ExprResult leftRtrn, rightRtrn;
        ExprDef *left, *right;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type == TypeString) {
                const char *str;
                str = xkb_atom_text(ctx, expr->value.str);
                if ((str != NULL) && (strlen(str) == 1)) {
                    val_rtrn->uval = str[0] * XkbGeomPtsPerMM;
                    return true;
                }
            }
            if (expr->type != TypeInt) {
                ERROR("Found constant of type %s, expected a number\n",
                      exprTypeText(expr->type));
                return false;
            }
            val_rtrn->ival = expr->value.ival;
            if (expr->type == TypeInt)
                val_rtrn->ival *= XkbGeomPtsPerMM;
            return true;
    
        case ExprIdent:
            ERROR("Numeric identifier \"%s\" unknown\n",
                  xkb_atom_text(ctx, expr->value.str));
            return ok;
    
        case ExprFieldRef:
            ERROR("Numeric default \"%s.%s\" unknown\n",
                  xkb_atom_text(ctx, expr->value.field.element),
                  xkb_atom_text(ctx, expr->value.field.field));
            return false;
    
        case OpAdd:
        case OpSubtract:
        case OpMultiply:
        case OpDivide:
            left = expr->value.binary.left;
            right = expr->value.binary.right;
            if (ExprResolveFloat(ctx, left, &leftRtrn) &&
                ExprResolveFloat(ctx, right, &rightRtrn)) {
                switch (expr->op) {
                case OpAdd:
                    val_rtrn->ival = leftRtrn.ival + rightRtrn.ival;
                    break;
    
                case OpSubtract:
                    val_rtrn->ival = leftRtrn.ival - rightRtrn.ival;
                    break;
    
                case OpMultiply:
                    val_rtrn->ival = leftRtrn.ival * rightRtrn.ival;
                    break;
    
                case OpDivide:
                    val_rtrn->ival = leftRtrn.ival / rightRtrn.ival;
                    break;
                }
                return true;
            }
            return false;
    
        case OpAssign:
            WSGO("Assignment operator not implemented yet\n");
            break;
    
        case OpNot:
            ERROR("The ! operator cannot be applied to a number\n");
            return false;
    
        case OpInvert:
        case OpNegate:
            left = expr->value.child;
            if (ExprResolveFloat(ctx, left, &leftRtrn)) {
                if (expr->op == OpNegate)
                    val_rtrn->ival = -leftRtrn.ival;
                else
                    val_rtrn->ival = ~leftRtrn.ival;
                return true;
            }
            return false;
    
        case OpUnaryPlus:
            left = expr->value.child;
            return ExprResolveFloat(ctx, left, val_rtrn);
    
        default:
            WSGO("Unknown operator %d in ResolveFloat\n", expr->op);
            break;
        }
        return false;
    }
    
    int
    ExprResolveKeyCode(struct xkb_context *ctx, ExprDef *expr,
                       ExprResult *val_rtrn)
    {
        ExprResult leftRtrn, rightRtrn;
        ExprDef *left, *right;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type != TypeInt) {
                ERROR
                    ("Found constant of type %s where an int was expected\n",
                    exprTypeText(expr->type));
                return false;
            }
            val_rtrn->uval = expr->value.uval;
            return true;
    
        case OpAdd:
        case OpSubtract:
        case OpMultiply:
        case OpDivide:
            left = expr->value.binary.left;
            right = expr->value.binary.right;
            if (ExprResolveKeyCode(ctx, left, &leftRtrn) &&
                ExprResolveKeyCode(ctx, right, &rightRtrn)) {
                switch (expr->op) {
                case OpAdd:
                    val_rtrn->uval = leftRtrn.uval + rightRtrn.uval;
                    break;
                case OpSubtract:
                    val_rtrn->uval = leftRtrn.uval - rightRtrn.uval;
                    break;
                case OpMultiply:
                    val_rtrn->uval = leftRtrn.uval * rightRtrn.uval;
                    break;
                case OpDivide:
                    val_rtrn->uval = leftRtrn.uval / rightRtrn.uval;
                    break;
                }
                return true;
            }
            return false;
    
        case OpNegate:
            left = expr->value.child;
            if (ExprResolveKeyCode(ctx, left, &leftRtrn)) {
                val_rtrn->uval = ~leftRtrn.uval;
                return true;
            }
            return false;
    
        case OpUnaryPlus:
            left = expr->value.child;
            return ExprResolveKeyCode(ctx, left, val_rtrn);
    
        default:
            WSGO("Unknown operator %d in ResolveKeyCode\n", expr->op);
            break;
        }
        return false;
    }
    
    /**
     * This function returns ... something.  It's a bit of a guess, really.
     *
     * If a string is given in value ctx, its first character will be
     * returned in uval.  If an integer is given in value ctx, it will be
     * returned in ival.  If a float is given in value ctx, it will be
     * returned as millimetres (rather than points) in ival.
     *
     * If an ident or field reference is given, the lookup function (if given)
     * will be called.  At the moment, only SimpleLookup use this, and they both
     * return the results in uval.  And don't support field references.
     *
     * Cool.
     */
    static int
    ExprResolveIntegerLookup(struct xkb_context *ctx, ExprDef *expr,
                             ExprResult *val_rtrn, IdentLookupFunc lookup,
                             const void *lookupPriv)
    {
        int ok = 0;
        ExprResult leftRtrn, rightRtrn;
        ExprDef *left, *right;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type == TypeString) {
                const char *str;
                str = xkb_atom_text(ctx, expr->value.str);
                if (str != NULL)
                    switch (strlen(str)) {
                    case 0:
                        val_rtrn->uval = 0;
                        return true;
                    case 1:
                        val_rtrn->uval = str[0];
                        return true;
                    default:
                        break;
                    }
            }
            if (expr->type != TypeInt) {
                ERROR
                    ("Found constant of type %s where an int was expected\n",
                    exprTypeText(expr->type));
                return false;
            }
            val_rtrn->ival = expr->value.ival;
            return true;
    
        case ExprIdent:
            if (lookup)
                ok = lookup(ctx, lookupPriv, expr->value.str,
                            TypeInt, val_rtrn);
            if (!ok)
                ERROR("Identifier \"%s\" of type int is unknown\n",
                      xkb_atom_text(ctx, expr->value.str));
            return ok;
    
        case ExprFieldRef:
            ERROR("Default \"%s.%s\" of type int is unknown\n",
                  xkb_atom_text(ctx, expr->value.field.element),
                  xkb_atom_text(ctx, expr->value.field.field));
            return false;
    
        case OpAdd:
        case OpSubtract:
        case OpMultiply:
        case OpDivide:
            left = expr->value.binary.left;
            right = expr->value.binary.right;
            if (ExprResolveIntegerLookup(ctx, left, &leftRtrn, lookup,
                                         lookupPriv) &&
                ExprResolveIntegerLookup(ctx, right, &rightRtrn, lookup,
                                         lookupPriv)) {
                switch (expr->op) {
                case OpAdd:
                    val_rtrn->ival = leftRtrn.ival + rightRtrn.ival;
                    break;
                case OpSubtract:
                    val_rtrn->ival = leftRtrn.ival - rightRtrn.ival;
                    break;
                case OpMultiply:
                    val_rtrn->ival = leftRtrn.ival * rightRtrn.ival;
                    break;
                case OpDivide:
                    val_rtrn->ival = leftRtrn.ival / rightRtrn.ival;
                    break;
                }
                return true;
            }
            return false;
    
        case OpAssign:
            WSGO("Assignment operator not implemented yet\n");
            break;
    
        case OpNot:
            ERROR("The ! operator cannot be applied to an integer\n");
            return false;
    
        case OpInvert:
        case OpNegate:
            left = expr->value.child;
            if (ExprResolveIntegerLookup(ctx, left, &leftRtrn, lookup,
                                         lookupPriv)) {
                if (expr->op == OpNegate)
                    val_rtrn->ival = -leftRtrn.ival;
                else
                    val_rtrn->ival = ~leftRtrn.ival;
                return true;
            }
            return false;
    
        case OpUnaryPlus:
            left = expr->value.child;
            return ExprResolveIntegerLookup(ctx, left, val_rtrn, lookup,
                                            lookupPriv);
    
        default:
            WSGO("Unknown operator %d in ResolveInteger\n", expr->op);
            break;
        }
        return false;
    }
    
    int
    ExprResolveInteger(struct xkb_context *ctx, ExprDef *expr,
                       ExprResult *val_rtrn)
    {
        return ExprResolveIntegerLookup(ctx, expr, val_rtrn, NULL, NULL);
    }
    
    int
    ExprResolveGroup(struct xkb_context *ctx, ExprDef *expr,
                     ExprResult *val_rtrn)
    {
        int ret;
        static const LookupEntry group_names[] = {
            { "group1", 1 },
            { "group2", 2 },
            { "group3", 3 },
            { "group4", 4 },
            { "group5", 5 },
            { "group6", 6 },
            { "group7", 7 },
            { "group8", 8 },
            { NULL, 0 }
        };
    
        ret = ExprResolveIntegerLookup(ctx, expr, val_rtrn, SimpleLookup,
                                       group_names);
        if (ret == false)
            return ret;
    
        if (val_rtrn->uval == 0 || val_rtrn->uval > XkbNumKbdGroups) {
            ERROR("Group index %u is out of range (1..%d)\n",
                  val_rtrn->uval, XkbNumKbdGroups);
            return false;
        }
    
        return true;
    }
    
    int
    ExprResolveLevel(struct xkb_context *ctx, ExprDef *expr,
                     ExprResult *val_rtrn)
    {
        int ret;
        static const LookupEntry level_names[] = {
            { "level1", 1 },
            { "level2", 2 },
            { "level3", 3 },
            { "level4", 4 },
            { "level5", 5 },
            { "level6", 6 },
            { "level7", 7 },
            { "level8", 8 },
            { NULL, 0 }
        };
    
        ret = ExprResolveIntegerLookup(ctx, expr, val_rtrn, SimpleLookup,
                                       level_names);
        if (ret == false)
            return ret;
    
        if (val_rtrn->ival < 1 || val_rtrn->ival > XkbMaxShiftLevel) {
            ERROR("Shift level %d is out of range (1..%d)\n", val_rtrn->ival,
                  XkbMaxShiftLevel);
            return false;
        }
    
        return true;
    }
    
    int
    ExprResolveButton(struct xkb_context *ctx, ExprDef *expr,
                      ExprResult *val_rtrn)
    {
        static const LookupEntry button_names[] = {
            { "button1", 1 },
            { "button2", 2 },
            { "button3", 3 },
            { "button4", 4 },
            { "button5", 5 },
            { "default", 0 },
            { NULL, 0 }
        };
    
        return ExprResolveIntegerLookup(ctx, expr, val_rtrn, SimpleLookup,
                                        button_names);
    }
    
    int
    ExprResolveString(struct xkb_context *ctx, ExprDef *expr,
                      ExprResult *val_rtrn)
    {
        ExprResult leftRtrn, rightRtrn;
        ExprDef *left;
        ExprDef *right;
        const char *bogus = NULL;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type != TypeString) {
                ERROR("Found constant of type %s, expected a string\n",
                      exprTypeText(expr->type));
                return false;
            }
            val_rtrn->str = xkb_atom_strdup(ctx, expr->value.str);
            if (val_rtrn->str == NULL)
                val_rtrn->str = strdup("");
            return true;
    
        case ExprIdent:
            ERROR("Identifier \"%s\" of type string not found\n",
                  xkb_atom_text(ctx, expr->value.str));
            return false;
    
        case ExprFieldRef:
            ERROR("Default \"%s.%s\" of type string not found\n",
                  xkb_atom_text(ctx, expr->value.field.element),
                  xkb_atom_text(ctx, expr->value.field.field));
            return false;
    
        case OpAdd:
            left = expr->value.binary.left;
            right = expr->value.binary.right;
            if (ExprResolveString(ctx, left, &leftRtrn) &&
                ExprResolveString(ctx, right, &rightRtrn)) {
                int len;
                char *new;
                len = strlen(leftRtrn.str) + strlen(rightRtrn.str) + 1;
                new = malloc(len);
                if (new) {
                    sprintf(new, "%s%s", leftRtrn.str, rightRtrn.str);
                    free(leftRtrn.str);
                    free(rightRtrn.str);
                    val_rtrn->str = new;
                    return true;
                }
                free(leftRtrn.str);
                free(rightRtrn.str);
            }
            return false;
    
        case OpSubtract:
            if (bogus == NULL)
                bogus = "Subtraction";
        case OpMultiply:
            if (bogus == NULL)
                bogus = "Multiplication";
        case OpDivide:
            if (bogus == NULL)
                bogus = "Division";
        case OpAssign:
            if (bogus == NULL)
                bogus = "Assignment";
        case OpNegate:
            if (bogus == NULL)
                bogus = "Negation";
        case OpInvert:
            if (bogus == NULL)
                bogus = "Bitwise complement";
            ERROR("%s of string values not permitted\n", bogus);
            return false;
    
        case OpNot:
            ERROR("The ! operator cannot be applied to a string\n");
            return false;
    
        case OpUnaryPlus:
            ERROR("The + operator cannot be applied to a string\n");
            return false;
    
        default:
            WSGO("Unknown operator %d in ResolveString\n", expr->op);
            break;
        }
        return false;
    }
    
    int
    ExprResolveKeyName(struct xkb_context *ctx, ExprDef *expr,
                       ExprResult *val_rtrn)
    {
        const char *bogus = NULL;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type != TypeKeyName) {
                ERROR("Found constant of type %s, expected a key name\n",
                      exprTypeText(expr->type));
                return false;
            }
            memcpy(val_rtrn->name, expr->value.keyName, XkbKeyNameLength);
            return true;
    
        case ExprIdent:
            ERROR("Identifier \"%s\" of type string not found\n",
                  xkb_atom_text(ctx, expr->value.str));
            return false;
    
        case ExprFieldRef:
            ERROR("Default \"%s.%s\" of type key name not found\n",
                  xkb_atom_text(ctx, expr->value.field.element),
                  xkb_atom_text(ctx, expr->value.field.field));
            return false;
    
        case OpAdd:
            if (bogus == NULL)
                bogus = "Addition";
        case OpSubtract:
            if (bogus == NULL)
                bogus = "Subtraction";
        case OpMultiply:
            if (bogus == NULL)
                bogus = "Multiplication";
        case OpDivide:
            if (bogus == NULL)
                bogus = "Division";
        case OpAssign:
            if (bogus == NULL)
                bogus = "Assignment";
        case OpNegate:
            if (bogus == NULL)
                bogus = "Negation";
        case OpInvert:
            if (bogus == NULL)
                bogus = "Bitwise complement";
            ERROR("%s of key name values not permitted\n", bogus);
            return false;
    
        case OpNot:
            ERROR("The ! operator cannot be applied to a key name\n");
            return false;
    
        case OpUnaryPlus:
            ERROR("The + operator cannot be applied to a key name\n");
            return false;
    
        default:
            WSGO("Unknown operator %d in ResolveKeyName\n", expr->op);
            break;
        }
        return false;
    }
    
    /***====================================================================***/
    
    int
    ExprResolveEnum(struct xkb_context *ctx, ExprDef *expr,
                    ExprResult *val_rtrn, const LookupEntry *values)
    {
        if (expr->op != ExprIdent) {
            ERROR("Found a %s where an enumerated value was expected\n",
                  exprOpText(expr->op));
            return false;
        }
        if (!SimpleLookup(ctx, values, expr->value.str, TypeInt, val_rtrn)) {
            int nOut = 0;
            ERROR("Illegal identifier %s (expected one of: ",
                  xkb_atom_text(ctx, expr->value.str));
            while (values && values->name)
            {
                if (nOut != 0)
                    INFO(", %s", values->name);
                else
                    INFO("%s", values->name);
                values++;
                nOut++;
            }
            INFO(")\n");
            return false;
        }
        return true;
    }
    
    static int
    ExprResolveMaskLookup(struct xkb_context *ctx, ExprDef *expr,
                          ExprResult *val_rtrn, IdentLookupFunc lookup,
                          const void *lookupPriv)
    {
        int ok = 0;
        ExprResult leftRtrn, rightRtrn;
        ExprDef *left, *right;
        const char *bogus = NULL;
    
        switch (expr->op) {
        case ExprValue:
            if (expr->type != TypeInt) {
                ERROR
                    ("Found constant of type %s where a mask was expected\n",
                    exprTypeText(expr->type));
                return false;
            }
            val_rtrn->ival = expr->value.ival;
            return true;
    
        case ExprIdent:
            ok = lookup(ctx, lookupPriv, expr->value.str, TypeInt, val_rtrn);
            if (!ok)
                ERROR("Identifier \"%s\" of type int is unknown\n",
                      xkb_atom_text(ctx, expr->value.str));
            return ok;
    
        case ExprFieldRef:
            ERROR("Default \"%s.%s\" of type int is unknown\n",
                  xkb_atom_text(ctx, expr->value.field.element),
                  xkb_atom_text(ctx, expr->value.field.field));
            return false;
    
        case ExprArrayRef:
            bogus = "array reference";
    
        case ExprActionDecl:
            if (bogus == NULL)
                bogus = "function use";
            ERROR("Unexpected %s in mask expression\n", bogus);
            ACTION("Expression ignored\n");
            return false;
    
        case OpAdd:
        case OpSubtract:
        case OpMultiply:
        case OpDivide:
            left = expr->value.binary.left;
            right = expr->value.binary.right;
            if (ExprResolveMaskLookup(ctx, left, &leftRtrn, lookup,
                                      lookupPriv) &&
                ExprResolveMaskLookup(ctx, right, &rightRtrn, lookup,
                                      lookupPriv)) {
                switch (expr->op) {
                case OpAdd:
                    val_rtrn->ival = leftRtrn.ival | rightRtrn.ival;
                    break;
                case OpSubtract:
                    val_rtrn->ival = leftRtrn.ival & (~rightRtrn.ival);
                    break;
                case OpMultiply:
                case OpDivide:
                    ERROR("Cannot %s masks\n",
                          expr->op == OpDivide ? "divide" : "multiply");
                    ACTION("Illegal operation ignored\n");
                    return false;
                }
                return true;
            }
            return false;
    
        case OpAssign:
            WSGO("Assignment operator not implemented yet\n");
            break;
    
        case OpInvert:
            left = expr->value.child;
            if (ExprResolveIntegerLookup(ctx, left, &leftRtrn, lookup,
                                         lookupPriv)) {
                val_rtrn->ival = ~leftRtrn.ival;
                return true;
            }
            return false;
    
        case OpUnaryPlus:
        case OpNegate:
        case OpNot:
            left = expr->value.child;
            if (ExprResolveIntegerLookup(ctx, left, &leftRtrn, lookup,
                                         lookupPriv)) {
                ERROR("The %s operator cannot be used with a mask\n",
                      (expr->op == OpNegate ? "-" : "!"));
            }
            return false;
    
        default:
            WSGO("Unknown operator %d in ResolveMask\n", expr->op);
            break;
        }
        return false;
    }
    
    int
    ExprResolveMask(struct xkb_context *ctx, ExprDef *expr,
                    ExprResult *val_rtrn, const LookupEntry *values)
    {
        return ExprResolveMaskLookup(ctx, expr, val_rtrn, SimpleLookup, values);
    }
    
    int
    ExprResolveModMask(struct xkb_context *ctx, ExprDef *expr,
                       ExprResult *val_rtrn)
    {
        return ExprResolveMaskLookup(ctx, expr, val_rtrn, LookupModMask, NULL);
    }
    
    int
    ExprResolveVModMask(ExprDef *expr, ExprResult *val_rtrn,
                        struct xkb_keymap *keymap)
    {
        return ExprResolveMaskLookup(keymap->ctx, expr, val_rtrn, LookupVModMask,
                                     keymap);
    }
    
    int
    ExprResolveKeySym(struct xkb_context *ctx, ExprDef *expr,
                      ExprResult *val_rtrn)
    {
        int ok = 0;
        xkb_keysym_t sym;
    
        if (expr->op == ExprIdent) {
            const char *str;
            str = xkb_atom_text(ctx, expr->value.str);
            if (str) {
                sym = xkb_keysym_from_name(str);
                if (sym != XKB_KEY_NoSymbol) {
                    val_rtrn->uval = sym;
                    return true;
                }
            }
        }
        ok = ExprResolveInteger(ctx, expr, val_rtrn);
        if ((ok) && (val_rtrn->uval < 10))
            val_rtrn->uval += '0';
        return ok;
    }