Structured log messages with a message registry
[platform/upstream/libxkbcommon.git] / src / xkbcomp / parser.y
index 7b28552..1beb30b 100644 (file)
 
  ********************************************************/
 
+/*
+ * The parser should work with reasonably recent versions of either
+ * bison or byacc.  So if you make changes, try to make sure it works
+ * in both!
+ */
+
 %{
-#include "xkbcomp-priv.h"
-#include "parseutils.h"
+#include "config.h"
+
+#include "xkbcomp/xkbcomp-priv.h"
+#include "xkbcomp/ast-build.h"
+#include "xkbcomp/parser-priv.h"
+#include "scanner-utils.h"
+#include "keysym.h"
+
+struct parser_param {
+    struct xkb_context *ctx;
+    struct scanner *scanner;
+    XkbFile *rtrn;
+    bool more_maps;
+};
+
+#define parser_err(param, error_id, fmt, ...) \
+    scanner_err_with_code((param)->scanner, error_id, fmt, ##__VA_ARGS__)
+
+#define parser_warn(param, warning_id, fmt, ...) \
+    scanner_warn_with_code((param)->scanner, warning_id, fmt, ##__VA_ARGS__)
+
+static void
+_xkbcommon_error(struct parser_param *param, const char *msg)
+{
+    parser_err(param, XKB_ERROR_INVALID_SYNTAX, "%s", msg);
+}
+
+static bool
+resolve_keysym(const char *name, xkb_keysym_t *sym_rtrn)
+{
+    xkb_keysym_t sym;
+
+    if (!name || istreq(name, "any") || istreq(name, "nosymbol")) {
+        *sym_rtrn = XKB_KEY_NoSymbol;
+        return true;
+    }
+
+    if (istreq(name, "none") || istreq(name, "voidsymbol")) {
+        *sym_rtrn = XKB_KEY_VoidSymbol;
+        return true;
+    }
+
+    sym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS);
+    if (sym != XKB_KEY_NoSymbol) {
+        *sym_rtrn = sym;
+        return true;
+    }
 
-extern int yylex(union YYSTYPE *val, struct YYLTYPE *loc, void *scanner);
+    return false;
+}
 
-#define scanner param->scanner
+#define param_scanner param->scanner
 %}
 
-%define                api.pure
-%locations
-%lex-param     { void *scanner }
-%parse-param   { struct parser_param *param }
+%define api.pure
+%lex-param      { struct scanner *param_scanner }
+%parse-param    { struct parser_param *param }
 
 %token
-       END_OF_FILE     0
-       ERROR_TOK       255
-       XKB_KEYMAP      1
-       XKB_KEYCODES    2
-       XKB_TYPES       3
-       XKB_SYMBOLS     4
-       XKB_COMPATMAP   5
-       XKB_GEOMETRY    6
-       XKB_SEMANTICS   7
-       XKB_LAYOUT      8
-       INCLUDE         10
-       OVERRIDE        11
-       AUGMENT         12
-       REPLACE         13
-       ALTERNATE       14
-       VIRTUAL_MODS    20
-       TYPE            21
-       INTERPRET       22
-       ACTION_TOK      23
-       KEY             24
-       ALIAS           25
-       GROUP           26
-       MODIFIER_MAP    27
-       INDICATOR       28
-       SHAPE           29
-       KEYS            30
-       ROW             31
-       SECTION         32
-       OVERLAY         33
-       TEXT            34
-       OUTLINE         35
-       SOLID           36
-       LOGO            37
-       VIRTUAL         38
-       EQUALS          40
-       PLUS            41
-       MINUS           42
-       DIVIDE          43
-       TIMES           44
-       OBRACE          45
-       CBRACE          46
-       OPAREN          47
-       CPAREN          48
-       OBRACKET        49
-       CBRACKET        50
-       DOT             51
-       COMMA           52
-       SEMI            53
-       EXCLAM          54
-       INVERT          55
-       STRING          60
-       INTEGER         61
-       FLOAT           62
-       IDENT           63
-       KEYNAME         64
-       PARTIAL         70
-       DEFAULT         71
-       HIDDEN          72
-       ALPHANUMERIC_KEYS       73
-       MODIFIER_KEYS           74
-       KEYPAD_KEYS             75
-       FUNCTION_KEYS           76
-       ALTERNATE_GROUP         77
-
-%right EQUALS
-%left  PLUS MINUS
-%left  TIMES DIVIDE
-%left  EXCLAM INVERT
-%left  OPAREN
-%start XkbFile
-%union {
-       int              ival;
-       unsigned         uval;
-       int64_t          num;
+        END_OF_FILE     0
+        ERROR_TOK       255
+        XKB_KEYMAP      1
+        XKB_KEYCODES    2
+        XKB_TYPES       3
+        XKB_SYMBOLS     4
+        XKB_COMPATMAP   5
+        XKB_GEOMETRY    6
+        XKB_SEMANTICS   7
+        XKB_LAYOUT      8
+        INCLUDE         10
+        OVERRIDE        11
+        AUGMENT         12
+        REPLACE         13
+        ALTERNATE       14
+        VIRTUAL_MODS    20
+        TYPE            21
+        INTERPRET       22
+        ACTION_TOK      23
+        KEY             24
+        ALIAS           25
+        GROUP           26
+        MODIFIER_MAP    27
+        INDICATOR       28
+        SHAPE           29
+        KEYS            30
+        ROW             31
+        SECTION         32
+        OVERLAY         33
+        TEXT            34
+        OUTLINE         35
+        SOLID           36
+        LOGO            37
+        VIRTUAL         38
+        EQUALS          40
+        PLUS            41
+        MINUS           42
+        DIVIDE          43
+        TIMES           44
+        OBRACE          45
+        CBRACE          46
+        OPAREN          47
+        CPAREN          48
+        OBRACKET        49
+        CBRACKET        50
+        DOT             51
+        COMMA           52
+        SEMI            53
+        EXCLAM          54
+        INVERT          55
+        STRING          60
+        INTEGER         61
+        FLOAT           62
+        IDENT           63
+        KEYNAME         64
+        PARTIAL         70
+        DEFAULT         71
+        HIDDEN          72
+        ALPHANUMERIC_KEYS       73
+        MODIFIER_KEYS           74
+        KEYPAD_KEYS             75
+        FUNCTION_KEYS           76
+        ALTERNATE_GROUP         77
+
+%right  EQUALS
+%left   PLUS MINUS
+%left   TIMES DIVIDE
+%left   EXCLAM INVERT
+%left   OPAREN
+
+%start  XkbFile
+
+%union  {
+        int64_t          num;
         enum xkb_file_type file_type;
-       char            *str;
-       xkb_atom_t      sval;
-       ParseCommon     *any;
-       ExprDef         *expr;
-       VarDef          *var;
-       VModDef         *vmod;
-       InterpDef       *interp;
-       KeyTypeDef      *keyType;
-       SymbolsDef      *syms;
-       ModMapDef       *modMask;
-       GroupCompatDef  *groupCompat;
-       IndicatorMapDef *ledMap;
-       IndicatorNameDef *ledName;
-       KeycodeDef      *keyName;
-       KeyAliasDef     *keyAlias;
+        char            *str;
+        xkb_atom_t      atom;
+        enum merge_mode merge;
+        enum xkb_map_flags mapFlags;
+        xkb_keysym_t    keysym;
+        ParseCommon     *any;
+        struct { ParseCommon *head; ParseCommon *last; } anyList;
+        ExprDef         *expr;
+        struct { ExprDef *head; ExprDef *last; } exprList;
+        VarDef          *var;
+        struct { VarDef *head; VarDef *last; } varList;
+        VModDef         *vmod;
+        struct { VModDef *head; VModDef *last; } vmodList;
+        InterpDef       *interp;
+        KeyTypeDef      *keyType;
+        SymbolsDef      *syms;
+        ModMapDef       *modMask;
+        GroupCompatDef  *groupCompat;
+        LedMapDef       *ledMap;
+        LedNameDef      *ledName;
+        KeycodeDef      *keyCode;
+        KeyAliasDef     *keyAlias;
         void            *geom;
-       XkbFile         *file;
+        XkbFile         *file;
+        struct { XkbFile *head; XkbFile *last; } fileList;
 }
+
 %type <num>     INTEGER FLOAT
-%type <str>     IDENT KEYNAME STRING
-%type <ival>   Number Integer Float SignedNumber
-%type <uval>   MergeMode OptMergeMode
+%type <str>     IDENT STRING
+%type <atom>    KEYNAME
+%type <num>     KeyCode Number Integer Float SignedNumber DoodadType
+%type <merge>   MergeMode OptMergeMode
 %type <file_type> XkbCompositeType FileType
-%type <uval>   DoodadType Flag Flags OptFlags KeyCode
-%type <str>    KeyName MapName OptMapName KeySym
-%type <sval>   FieldSpec Ident Element String
-%type <any>    DeclList Decl
-%type <expr>   OptExprList ExprList Expr Term Lhs Terminal ArrayInit KeySyms
-%type <expr>   OptKeySymList KeySymList Action ActionList Coord CoordList
-%type <var>    VarDecl VarDeclList SymbolsBody SymbolsVarDecl
-%type <vmod>   VModDecl VModDefList VModDef
-%type <interp> InterpretDecl InterpretMatch
-%type <keyType>        KeyTypeDecl
-%type <syms>   SymbolsDecl
-%type <modMask>        ModMapDecl
+%type <mapFlags> Flag Flags OptFlags
+%type <str>     MapName OptMapName
+%type <atom>    FieldSpec Ident Element String
+%type <keysym>  KeySym
+%type <any>     Decl
+%type <anyList> DeclList
+%type <expr>    Expr Term Lhs Terminal ArrayInit KeySyms
+%type <expr>    OptKeySymList KeySymList Action Coord CoordList
+%type <exprList> OptExprList ExprList ActionList
+%type <var>     VarDecl SymbolsVarDecl
+%type <varList> VarDeclList SymbolsBody
+%type <vmod>    VModDef
+%type <vmodList> VModDefList VModDecl
+%type <interp>  InterpretDecl InterpretMatch
+%type <keyType> KeyTypeDecl
+%type <syms>    SymbolsDecl
+%type <modMask> ModMapDecl
 %type <groupCompat> GroupCompatDecl
-%type <ledMap> IndicatorMapDecl
-%type <ledName>        IndicatorNameDecl
-%type <keyName>        KeyNameDecl
+%type <ledMap>  LedMapDecl
+%type <ledName> LedNameDecl
+%type <keyCode> KeyNameDecl
 %type <keyAlias> KeyAliasDecl
-%type <geom>   ShapeDecl SectionDecl SectionBody SectionBodyItem RowBody RowBodyItem
+%type <geom>    ShapeDecl SectionDecl SectionBody SectionBodyItem RowBody RowBodyItem
 %type <geom>    Keys Key OverlayDecl OverlayKeyList OverlayKey OutlineList OutlineInList
 %type <geom>    DoodadDecl
-%type <file>   XkbFile XkbMapConfigList XkbMapConfig XkbConfig
-%type <file>   XkbCompositeMap XkbCompMapList
+%type <file>    XkbFile XkbMapConfig
+%type <fileList> XkbMapConfigList
+%type <file>    XkbCompositeMap
+
+%destructor { FreeStmt((ParseCommon *) $$); }
+    <any> <expr> <var> <vmod> <interp> <keyType> <syms> <modMask> <groupCompat>
+    <ledMap> <ledName> <keyCode> <keyAlias>
+%destructor { FreeStmt((ParseCommon *) $$.head); }
+    <anyList> <exprList> <varList> <vmodList>
+/* The destructor also runs on the start symbol when the parser *succeeds*.
+ * The `if` here catches this case. */
+%destructor { if (!param->rtrn) FreeXkbFile($$); } <file>
+%destructor { FreeXkbFile($$.head); } <fileList>
+%destructor { free($$); } <str>
+
 %%
-XkbFile                :       XkbCompMapList
-                       { $$= param->rtrn= $1; }
-               |       XkbMapConfigList
-                       { $$= param->rtrn= $1;  }
-               |       XkbConfig
-                       { $$= param->rtrn= $1; }
-               ;
-
-XkbCompMapList :       XkbCompMapList XkbCompositeMap
-                       { $$= (XkbFile *)AppendStmt(&$1->common,&$2->common); }
-               |       XkbCompositeMap
-                       { $$= $1; }
-               ;
-
-XkbCompositeMap        :       OptFlags XkbCompositeType OptMapName OBRACE
-                           XkbMapConfigList
-                       CBRACE SEMI
-                       {
-                            $$ = CreateXKBFile(param->ctx, $2, $3,
-                                               &$5->common, $1);
-                        }
-               ;
-
-XkbCompositeType:      XKB_KEYMAP      { $$= FILE_TYPE_KEYMAP; }
-               |       XKB_SEMANTICS   { $$= FILE_TYPE_KEYMAP; }
-               |       XKB_LAYOUT      { $$= FILE_TYPE_KEYMAP; }
-               ;
-
-XkbMapConfigList :     XkbMapConfigList XkbMapConfig
-                       {
-                            if (!$2)
-                                $$= $1;
-                            else
-                                $$= (XkbFile *)AppendStmt(&$1->common,&$2->common);
+
+/*
+ * An actual file may contain more than one map. However, if we do things
+ * in the normal yacc way, i.e. aggregate all of the maps into a list and
+ * let the caller find the map it wants, we end up scanning and parsing a
+ * lot of unneeded maps (in the end we always just need one).
+ * Instead of doing that, we make yyparse return one map at a time, and
+ * then call it repeatedly until we find the map we need. Once we find it,
+ * we don't need to parse everything that follows in the file.
+ * This does mean that if we e.g. always use the first map, the file may
+ * contain complete garbage after that. But it's worth it.
+ */
+
+XkbFile         :       XkbCompositeMap
+                        { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; }
+                |       XkbMapConfig
+                        { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; YYACCEPT; }
+                |       END_OF_FILE
+                        { $$ = param->rtrn = NULL; param->more_maps = false; }
+                ;
+
+XkbCompositeMap :       OptFlags XkbCompositeType OptMapName OBRACE
+                            XkbMapConfigList
+                        CBRACE SEMI
+                        { $$ = XkbFileCreate($2, $3, (ParseCommon *) $5.head, $1); }
+                ;
+
+XkbCompositeType:       XKB_KEYMAP      { $$ = FILE_TYPE_KEYMAP; }
+                |       XKB_SEMANTICS   { $$ = FILE_TYPE_KEYMAP; }
+                |       XKB_LAYOUT      { $$ = FILE_TYPE_KEYMAP; }
+                ;
+
+XkbMapConfigList :      XkbMapConfigList XkbMapConfig
+                        { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; }
+                |       XkbMapConfig
+                        { $$.head = $$.last = $1; }
+                ;
+
+XkbMapConfig    :       OptFlags FileType OptMapName OBRACE
+                            DeclList
+                        CBRACE SEMI
+                        {
+                            $$ = XkbFileCreate($2, $3, $5.head, $1);
                         }
-               |       XkbMapConfig
-                       { $$= $1; }
-               ;
-
-XkbMapConfig   :       OptFlags FileType OptMapName OBRACE
-                           DeclList
-                       CBRACE SEMI
-                       {
-                            if ($2 == FILE_TYPE_GEOMETRY)
-                            {
-                                free($3);
-                                FreeStmt($5);
-                                $$= NULL;
-                            }
-                            else
-                            {
-                                $$ = CreateXKBFile(param->ctx, $2, $3, $5, $1);
+                ;
+
+FileType        :       XKB_KEYCODES            { $$ = FILE_TYPE_KEYCODES; }
+                |       XKB_TYPES               { $$ = FILE_TYPE_TYPES; }
+                |       XKB_COMPATMAP           { $$ = FILE_TYPE_COMPAT; }
+                |       XKB_SYMBOLS             { $$ = FILE_TYPE_SYMBOLS; }
+                |       XKB_GEOMETRY            { $$ = FILE_TYPE_GEOMETRY; }
+                ;
+
+OptFlags        :       Flags                   { $$ = $1; }
+                |                               { $$ = 0; }
+                ;
+
+Flags           :       Flags Flag              { $$ = ($1 | $2); }
+                |       Flag                    { $$ = $1; }
+                ;
+
+Flag            :       PARTIAL                 { $$ = MAP_IS_PARTIAL; }
+                |       DEFAULT                 { $$ = MAP_IS_DEFAULT; }
+                |       HIDDEN                  { $$ = MAP_IS_HIDDEN; }
+                |       ALPHANUMERIC_KEYS       { $$ = MAP_HAS_ALPHANUMERIC; }
+                |       MODIFIER_KEYS           { $$ = MAP_HAS_MODIFIER; }
+                |       KEYPAD_KEYS             { $$ = MAP_HAS_KEYPAD; }
+                |       FUNCTION_KEYS           { $$ = MAP_HAS_FN; }
+                |       ALTERNATE_GROUP         { $$ = MAP_IS_ALTGR; }
+                ;
+
+DeclList        :       DeclList Decl
+                        {
+                            if ($2) {
+                                if ($1.head) {
+                                    $$.head = $1.head; $1.last->next = $2; $$.last = $2;
+                                } else {
+                                    $$.head = $$.last = $2;
+                                }
                             }
                         }
-               ;
-
-XkbConfig      :       OptFlags FileType OptMapName DeclList
-                       {
-                            if ($2 == FILE_TYPE_GEOMETRY)
-                            {
-                                free($3);
-                                FreeStmt($4);
-                                $$= NULL;
-                            }
-                            else
-                            {
-                                $$ = CreateXKBFile(param->ctx, $2, $3, $4, $1);
+                        /*
+                         * VModDecl is "inlined" directly into DeclList, i.e.
+                         * each VModDef in the VModDecl is a separate Decl in
+                         * the File.
+                         */
+                |       DeclList OptMergeMode VModDecl
+                        {
+                            for (VModDef *vmod = $3.head; vmod; vmod = (VModDef *) vmod->common.next)
+                                vmod->merge = $2;
+                            if ($1.head) {
+                                $$.head = $1.head; $1.last->next = &$3.head->common; $$.last = &$3.last->common;
+                            } else {
+                                $$.head = &$3.head->common; $$.last = &$3.last->common;
                             }
                         }
-               ;
-
-
-FileType       :       XKB_KEYCODES            { $$= FILE_TYPE_KEYCODES; }
-               |       XKB_TYPES               { $$= FILE_TYPE_TYPES; }
-               |       XKB_COMPATMAP           { $$= FILE_TYPE_COMPAT; }
-               |       XKB_SYMBOLS             { $$= FILE_TYPE_SYMBOLS; }
-               |       XKB_GEOMETRY            { $$= FILE_TYPE_GEOMETRY; }
-               ;
-
-OptFlags       :       Flags                   { $$= $1; }
-               |                               { $$= 0; }
-               ;
-
-Flags          :       Flags Flag              { $$= (($1)|($2)); }
-               |       Flag                    { $$= $1; }
-               ;
-
-Flag           :       PARTIAL                 { $$= XkbLC_Partial; }
-               |       DEFAULT                 { $$= XkbLC_Default; }
-               |       HIDDEN                  { $$= XkbLC_Hidden; }
-               |       ALPHANUMERIC_KEYS       { $$= XkbLC_AlphanumericKeys; }
-               |       MODIFIER_KEYS           { $$= XkbLC_ModifierKeys; }
-               |       KEYPAD_KEYS             { $$= XkbLC_KeypadKeys; }
-               |       FUNCTION_KEYS           { $$= XkbLC_FunctionKeys; }
-               |       ALTERNATE_GROUP         { $$= XkbLC_AlternateGroup; }
-               ;
-
-DeclList       :       DeclList Decl
-                       { $$= AppendStmt($1,$2); }
-               |       { $$= NULL; }
-               ;
-
-Decl           :       OptMergeMode VarDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode VModDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode InterpretDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode KeyNameDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode KeyAliasDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode KeyTypeDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode SymbolsDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode ModMapDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode GroupCompatDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode IndicatorMapDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode IndicatorNameDecl
-                       {
-                           $2->merge= StmtSetMerge(&$2->common,$1,&@1,scanner);
-                           $$= &$2->common;
-                       }
-               |       OptMergeMode ShapeDecl
-                       {
-                       }
-               |       OptMergeMode SectionDecl
-                       {
-                       }
-               |       OptMergeMode DoodadDecl
-                       {
-                       }
-               |       MergeMode STRING
-                       {
-                           if ($1==MergeAltForm) {
-                               yyerror(&@1, scanner,
-                                        "cannot use 'alternate' to include other maps");
-                               $$= &IncludeCreate($2,MergeDefault)->common;
-                           }
-                           else {
-                               $$= &IncludeCreate($2,$1)->common;
-                           }
+                |       { $$.head = $$.last = NULL; }
+                ;
+
+Decl            :       OptMergeMode VarDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                /*      OptMergeMode VModDecl - see above. */
+                |       OptMergeMode InterpretDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode KeyNameDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode KeyAliasDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode KeyTypeDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode SymbolsDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode ModMapDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode GroupCompatDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode LedMapDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode LedNameDecl
+                        {
+                            $2->merge = $1;
+                            $$ = (ParseCommon *) $2;
+                        }
+                |       OptMergeMode ShapeDecl          { $$ = NULL; }
+                |       OptMergeMode SectionDecl        { $$ = NULL; }
+                |       OptMergeMode DoodadDecl         { $$ = NULL; }
+                |       MergeMode STRING
+                        {
+                            $$ = (ParseCommon *) IncludeCreate(param->ctx, $2, $1);
                             free($2);
                         }
-               ;
+                ;
 
-VarDecl                :       Lhs EQUALS Expr SEMI
-                       { $$= VarCreate($1,$3); }
-               |       Ident SEMI
-                       { $$= BoolVarCreate($1,1); }
-               |       EXCLAM Ident SEMI
-                       { $$= BoolVarCreate($2,0); }
-               ;
+VarDecl         :       Lhs EQUALS Expr SEMI
+                        { $$ = VarCreate($1, $3); }
+                |       Ident SEMI
+                        { $$ = BoolVarCreate($1, true); }
+                |       EXCLAM Ident SEMI
+                        { $$ = BoolVarCreate($2, false); }
+                ;
 
-KeyNameDecl    :       KeyName EQUALS KeyCode SEMI
-                        {
-                           KeycodeDef *def;
-
-                           def= KeycodeCreate($1,$3);
-                           free($1);
-                           $$= def;
-                       }
-               ;
-
-KeyAliasDecl   :       ALIAS KeyName EQUALS KeyName SEMI
-                       {
-                           KeyAliasDef *def;
-                           def= KeyAliasCreate($2,$4);
-                           free($2);
-                           free($4);
-                           $$= def;
-                       }
-               ;
-
-VModDecl       :       VIRTUAL_MODS VModDefList SEMI
-                       { $$= $2; }
-               ;
-
-VModDefList    :       VModDefList COMMA VModDef
-                       { $$= (VModDef *)AppendStmt(&$1->common,&$3->common); }
-               |       VModDef
-                       { $$= $1; }
-               ;
-
-VModDef                :       Ident
-                       { $$= VModCreate($1,NULL); }
-               |       Ident EQUALS Expr
-                       { $$= VModCreate($1,$3); }
-               ;
-
-InterpretDecl  :       INTERPRET InterpretMatch OBRACE
-                           VarDeclList
-                       CBRACE SEMI
-                       {
-                           $2->def= $4;
-                           $$= $2;
-                       }
-               ;
-
-InterpretMatch :       KeySym PLUS Expr        
-                       { $$= InterpCreate($1, $3); }
-               |       KeySym                  
-                       { $$= InterpCreate($1, NULL); }
-               ;
-
-VarDeclList    :       VarDeclList VarDecl
-                       { $$= (VarDef *)AppendStmt(&$1->common,&$2->common); }
-               |       VarDecl
-                       { $$= $1; }
-               ;
-
-KeyTypeDecl    :       TYPE String OBRACE
-                           VarDeclList
-                       CBRACE SEMI
-                       { $$= KeyTypeCreate($2,$4); }
-               ;
-
-SymbolsDecl    :       KEY KeyName OBRACE
-                           SymbolsBody
-                       CBRACE SEMI
-                       { $$= SymbolsCreate($2,(ExprDef *)$4); free($2); }
-               ;
-
-SymbolsBody    :       SymbolsBody COMMA SymbolsVarDecl
-                       { $$= (VarDef *)AppendStmt(&$1->common,&$3->common); }
-               |       SymbolsVarDecl
-                       { $$= $1; }
-               |       { $$= NULL; }
-               ;
-
-SymbolsVarDecl :       Lhs EQUALS Expr
-                       { $$= VarCreate($1,$3); }
-               |       Lhs EQUALS ArrayInit
-                       { $$= VarCreate($1,$3); }
-               |       Ident
-                       { $$= BoolVarCreate($1,1); }
-               |       EXCLAM Ident
-                       { $$= BoolVarCreate($2,0); }
-               |       ArrayInit
-                       { $$= VarCreate(NULL,$1); }
-               ;
-
-ArrayInit      :       OBRACKET OptKeySymList CBRACKET
-                       { $$= $2; }
-               |       OBRACKET ActionList CBRACKET
-                       { $$= ExprCreateUnary(ExprActionList,TypeAction,$2); }
-               ;
-
-GroupCompatDecl        :       GROUP Integer EQUALS Expr SEMI
-                       { $$= GroupCompatCreate($2,$4); }
-               ;
-
-ModMapDecl     :       MODIFIER_MAP Ident OBRACE ExprList CBRACE SEMI
-                       { $$= ModMapCreate($2,$4); }
-               ;
-
-IndicatorMapDecl:      INDICATOR String OBRACE VarDeclList CBRACE SEMI
-                       { $$= IndicatorMapCreate($2,$4); }
-               ;
-
-IndicatorNameDecl:     INDICATOR Integer EQUALS Expr SEMI
-                       { $$= IndicatorNameCreate($2,$4,false); }
-               |       VIRTUAL INDICATOR Integer EQUALS Expr SEMI
-                       { $$= IndicatorNameCreate($3,$5,true); }
-               ;
-
-ShapeDecl      :       SHAPE String OBRACE OutlineList CBRACE SEMI
-                       { $$= NULL; }
-               |       SHAPE String OBRACE CoordList CBRACE SEMI
-                       { $$= NULL; }
-               ;
-
-SectionDecl    :       SECTION String OBRACE SectionBody CBRACE SEMI
-                       { $$= NULL; }
-               ;
-
-SectionBody    :       SectionBody SectionBodyItem
-                       { $$= NULL;}
-               |       SectionBodyItem
-                       { $$= NULL; }
-               ;
-
-SectionBodyItem        :       ROW OBRACE RowBody CBRACE SEMI
-                       { $$= NULL; }
-               |       VarDecl
-                       { FreeStmt(&$1->common); $$= NULL; }
-               |       DoodadDecl
-                       { $$= NULL; }
-               |       IndicatorMapDecl
-                       { FreeStmt(&$1->common); $$= NULL; }
-               |       OverlayDecl
-                       { $$= NULL; }
-               ;
-
-RowBody                :       RowBody RowBodyItem
-                       { $$= NULL;}
-               |       RowBodyItem
-                       { $$= NULL; }
-               ;
-
-RowBodyItem    :       KEYS OBRACE Keys CBRACE SEMI
-                       { $$= NULL; }
-               |       VarDecl
-                       { FreeStmt(&$1->common); $$= NULL; }
-               ;
-
-Keys           :       Keys COMMA Key
-                       { $$= NULL; }
-               |       Key
-                       { $$= NULL; }
-               ;
-
-Key            :       KeyName
-                       { free($1); $$= NULL; }
-               |       OBRACE ExprList CBRACE
-                       { FreeStmt(&$2->common); $$= NULL; }
-               ;
-
-OverlayDecl    :       OVERLAY String OBRACE OverlayKeyList CBRACE SEMI
-                       { $$= NULL; }
-               ;
-
-OverlayKeyList :       OverlayKeyList COMMA OverlayKey
-                       { $$= NULL; }
-               |       OverlayKey
-                       { $$= NULL; }
-               ;
-
-OverlayKey     :       KeyName EQUALS KeyName
-                       { free($1); free($3); $$= NULL; }
-               ;
-
-OutlineList    :       OutlineList COMMA OutlineInList
-                       { $$= NULL;}
-               |       OutlineInList
-                       { $$= NULL; }
-               ;
-
-OutlineInList  :       OBRACE CoordList CBRACE
-                       { $$= NULL; }
-               |       Ident EQUALS OBRACE CoordList CBRACE
-                       { $$= NULL; }
-               |       Ident EQUALS Expr
-                       { FreeStmt(&$3->common); $$= NULL; }
-               ;
-
-CoordList      :       CoordList COMMA Coord
-                       { $$= NULL; }
-               |       Coord
-                       { $$= NULL; }
-               ;
-
-Coord          :       OBRACKET SignedNumber COMMA SignedNumber CBRACKET
-                       { $$= NULL; }
-               ;
-
-DoodadDecl     :       DoodadType String OBRACE VarDeclList CBRACE SEMI
-                       { FreeStmt(&$4->common); $$= NULL; }
-               ;
-
-DoodadType     :       TEXT                    { $$= 0; }
-               |       OUTLINE                 { $$= 0; }
-               |       SOLID                   { $$= 0; }
-               |       LOGO                    { $$= 0; }
-               ;
-
-FieldSpec      :       Ident                   { $$= $1; }
-               |       Element                 { $$= $1; }
-               ;
-
-Element                :       ACTION_TOK              
-                       { $$= xkb_atom_intern(param->ctx, "action"); }
-               |       INTERPRET
-                       { $$= xkb_atom_intern(param->ctx, "interpret"); }
-               |       TYPE
-                       { $$= xkb_atom_intern(param->ctx, "type"); }
-               |       KEY
-                       { $$= xkb_atom_intern(param->ctx, "key"); }
-               |       GROUP
-                       { $$= xkb_atom_intern(param->ctx, "group"); }
-               |       MODIFIER_MAP
-                       {$$= xkb_atom_intern(param->ctx, "modifier_map");}
-               |       INDICATOR
-                       { $$= xkb_atom_intern(param->ctx, "indicator"); }
-               |       SHAPE   
-                       { $$= xkb_atom_intern(param->ctx, "shape"); }
-               |       ROW     
-                       { $$= XKB_ATOM_NONE; }
-               |       SECTION 
-                       { $$= XKB_ATOM_NONE; }
-               |       TEXT
-                       { $$= XKB_ATOM_NONE; }
-               ;
-
-OptMergeMode   :       MergeMode               { $$= $1; }
-               |                               { $$= MergeDefault; }
-               ;
-
-MergeMode      :       INCLUDE                 { $$= MergeDefault; }
-               |       AUGMENT                 { $$= MergeAugment; }
-               |       OVERRIDE                { $$= MergeOverride; }
-               |       REPLACE                 { $$= MergeReplace; }
-               |       ALTERNATE               { $$= MergeAltForm; }
-               ;
-
-OptExprList    :       ExprList                        { $$= $1; }
-               |                               { $$= NULL; }
-               ;
-
-ExprList       :       ExprList COMMA Expr
-                       { $$= (ExprDef *)AppendStmt(&$1->common,&$3->common); }
-               |       Expr
-                       { $$= $1; }
-               ;
-
-Expr           :       Expr DIVIDE Expr
-                       { $$= ExprCreateBinary(OpDivide,$1,$3); }
-               |       Expr PLUS Expr
-                       { $$= ExprCreateBinary(OpAdd,$1,$3); }
-               |       Expr MINUS Expr
-                       { $$= ExprCreateBinary(OpSubtract,$1,$3); }
-               |       Expr TIMES Expr
-                       { $$= ExprCreateBinary(OpMultiply,$1,$3); }
-               |       Lhs EQUALS Expr
-                       { $$= ExprCreateBinary(OpAssign,$1,$3); }
-               |       Term
-                       { $$= $1; }
-               ;
-
-Term           :       MINUS Term
-                       { $$= ExprCreateUnary(OpNegate,$2->type,$2); }
-               |       PLUS Term
-                       { $$= ExprCreateUnary(OpUnaryPlus,$2->type,$2); }
-               |       EXCLAM Term
-                       { $$= ExprCreateUnary(OpNot,TypeBoolean,$2); }
-               |       INVERT Term
-                       { $$= ExprCreateUnary(OpInvert,$2->type,$2); }
-               |       Lhs
-                       { $$= $1;  }
-               |       FieldSpec OPAREN OptExprList CPAREN %prec OPAREN
-                       { $$= ActionCreate($1,$3); }
-               |       Terminal
-                       { $$= $1;  }
-               |       OPAREN Expr CPAREN
-                       { $$= $2;  }
-               ;
-
-ActionList     :       ActionList COMMA Action
-                       { $$= (ExprDef *)AppendStmt(&$1->common,&$3->common); }
-               |       Action
-                       { $$= $1; }
-               ;
-
-Action         :       FieldSpec OPAREN OptExprList CPAREN
-                       { $$= ActionCreate($1,$3); }
-               ;
-
-Lhs            :       FieldSpec
-                       {
-                           ExprDef *expr;
-                            expr= ExprCreate(ExprIdent,TypeUnknown);
-                            expr->value.str= $1;
-                            $$= expr;
-                       }
-               |       FieldSpec DOT FieldSpec
-                        {
-                            ExprDef *expr;
-                            expr= ExprCreate(ExprFieldRef,TypeUnknown);
-                            expr->value.field.element= $1;
-                            expr->value.field.field= $3;
-                            $$= expr;
-                       }
-               |       FieldSpec OBRACKET Expr CBRACKET
-                       {
-                           ExprDef *expr;
-                           expr= ExprCreate(ExprArrayRef,TypeUnknown);
-                           expr->value.array.element= XKB_ATOM_NONE;
-                           expr->value.array.field= $1;
-                           expr->value.array.entry= $3;
-                           $$= expr;
-                       }
-               |       FieldSpec DOT FieldSpec OBRACKET Expr CBRACKET
-                       {
-                           ExprDef *expr;
-                           expr= ExprCreate(ExprArrayRef,TypeUnknown);
-                           expr->value.array.element= $1;
-                           expr->value.array.field= $3;
-                           expr->value.array.entry= $5;
-                           $$= expr;
-                       }
-               ;
-
-Terminal       :       String
-                       {
-                           ExprDef *expr;
-                            expr= ExprCreate(ExprValue,TypeString);
-                            expr->value.str= $1;
-                            $$= expr;
-                       }
-               |       Integer
-                       {
-                           ExprDef *expr;
-                            expr= ExprCreate(ExprValue,TypeInt);
-                            expr->value.ival= $1;
-                            $$= expr;
-                       }
-               |       Float
-                       {
-                           $$= NULL;
-                       }
-               |       KeyName
-                       {
-                           ExprDef *expr;
-                           expr= ExprCreate(ExprValue,TypeKeyName);
-                           memset(expr->value.keyName,0,5);
-                           strncpy(expr->value.keyName,$1,4);
-                           free($1);
-                           $$= expr;
-                       }
-               ;
-
-OptKeySymList  :       KeySymList                      { $$= $1; }
-               |                                       { $$= NULL; }
-               ;
-
-KeySymList     :       KeySymList COMMA KeySym
-                       { $$= AppendKeysymList($1,$3); }
-                |       KeySymList COMMA KeySyms
-                        { $$= AppendMultiKeysymList($1,$3); }
-               |       KeySym
-                       { $$= CreateKeysymList($1); }
-                |       KeySyms
-                        { $$= CreateMultiKeysymList($1); }
-               ;
+KeyNameDecl     :       KEYNAME EQUALS KeyCode SEMI
+                        { $$ = KeycodeCreate($1, $3); }
+                ;
 
-KeySyms         :       OBRACE KeySymList CBRACE
-                        { $$= $2; }
+KeyAliasDecl    :       ALIAS KEYNAME EQUALS KEYNAME SEMI
+                        { $$ = KeyAliasCreate($2, $4); }
+                ;
+
+VModDecl        :       VIRTUAL_MODS VModDefList SEMI
+                        { $$ = $2; }
+                ;
+
+VModDefList     :       VModDefList COMMA VModDef
+                        { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
+                |       VModDef
+                        { $$.head = $$.last = $1; }
+                ;
+
+VModDef         :       Ident
+                        { $$ = VModCreate($1, NULL); }
+                |       Ident EQUALS Expr
+                        { $$ = VModCreate($1, $3); }
+                ;
+
+InterpretDecl   :       INTERPRET InterpretMatch OBRACE
+                            VarDeclList
+                        CBRACE SEMI
+                        { $2->def = $4.head; $$ = $2; }
+                ;
+
+InterpretMatch  :       KeySym PLUS Expr
+                        { $$ = InterpCreate($1, $3); }
+                |       KeySym
+                        { $$ = InterpCreate($1, NULL); }
+                ;
+
+VarDeclList     :       VarDeclList VarDecl
+                        { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; }
+                |       VarDecl
+                        { $$.head = $$.last = $1; }
+                ;
+
+KeyTypeDecl     :       TYPE String OBRACE
+                            VarDeclList
+                        CBRACE SEMI
+                        { $$ = KeyTypeCreate($2, $4.head); }
+                ;
+
+SymbolsDecl     :       KEY KEYNAME OBRACE
+                            SymbolsBody
+                        CBRACE SEMI
+                        { $$ = SymbolsCreate($2, $4.head); }
+                ;
+
+SymbolsBody     :       SymbolsBody COMMA SymbolsVarDecl
+                        { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
+                |       SymbolsVarDecl
+                        { $$.head = $$.last = $1; }
+                |       { $$.head = $$.last = NULL; }
+                ;
+
+SymbolsVarDecl  :       Lhs EQUALS Expr         { $$ = VarCreate($1, $3); }
+                |       Lhs EQUALS ArrayInit    { $$ = VarCreate($1, $3); }
+                |       Ident                   { $$ = BoolVarCreate($1, true); }
+                |       EXCLAM Ident            { $$ = BoolVarCreate($2, false); }
+                |       ArrayInit               { $$ = VarCreate(NULL, $1); }
+                ;
+
+ArrayInit       :       OBRACKET OptKeySymList CBRACKET
+                        { $$ = $2; }
+                |       OBRACKET ActionList CBRACKET
+                        { $$ = ExprCreateActionList($2.head); }
+                ;
+
+GroupCompatDecl :       GROUP Integer EQUALS Expr SEMI
+                        { $$ = GroupCompatCreate($2, $4); }
                 ;
 
-KeySym         :       IDENT   { $$= $1; }
-               |       SECTION { $$= strdup("section"); }
-               |       Integer         
-                       {
-                           if ($1 < 10) {      /* XK_0 .. XK_9 */
-                               $$= malloc(2);
-                               $$[0]= $1 + '0';
-                               $$[1]= '\0';
-                           }
-                           else {
-                               $$= malloc(17);
-                               snprintf($$, 17, "0x%x", $1);
-                           }
-                       }
-               ;
+ModMapDecl      :       MODIFIER_MAP Ident OBRACE ExprList CBRACE SEMI
+                        { $$ = ModMapCreate($2, $4.head); }
+                ;
+
+LedMapDecl:             INDICATOR String OBRACE VarDeclList CBRACE SEMI
+                        { $$ = LedMapCreate($2, $4.head); }
+                ;
+
+LedNameDecl:            INDICATOR Integer EQUALS Expr SEMI
+                        { $$ = LedNameCreate($2, $4, false); }
+                |       VIRTUAL INDICATOR Integer EQUALS Expr SEMI
+                        { $$ = LedNameCreate($3, $5, true); }
+                ;
+
+ShapeDecl       :       SHAPE String OBRACE OutlineList CBRACE SEMI
+                        { $$ = NULL; }
+                |       SHAPE String OBRACE CoordList CBRACE SEMI
+                        { (void) $4; $$ = NULL; }
+                ;
 
-SignedNumber   :       MINUS Number    { $$= -$2; }
-               |       Number              { $$= $1; }
-               ;
+SectionDecl     :       SECTION String OBRACE SectionBody CBRACE SEMI
+                        { $$ = NULL; }
+                ;
 
-Number         :       FLOAT           { $$= $1; }
-               |       INTEGER         { $$= $1*XkbGeomPtsPerMM; }
-               ;
+SectionBody     :       SectionBody SectionBodyItem     { $$ = NULL;}
+                |       SectionBodyItem                 { $$ = NULL; }
+                ;
 
-Float          :       FLOAT           { $$= 0; }
-               ;
+SectionBodyItem :       ROW OBRACE RowBody CBRACE SEMI
+                        { $$ = NULL; }
+                |       VarDecl
+                        { FreeStmt((ParseCommon *) $1); $$ = NULL; }
+                |       DoodadDecl
+                        { $$ = NULL; }
+                |       LedMapDecl
+                        { FreeStmt((ParseCommon *) $1); $$ = NULL; }
+                |       OverlayDecl
+                        { $$ = NULL; }
+                ;
 
-Integer                :       INTEGER         { $$= $1; }
-               ;
+RowBody         :       RowBody RowBodyItem     { $$ = NULL;}
+                |       RowBodyItem             { $$ = NULL; }
+                ;
 
-KeyCode         :       INTEGER         { $$= $1; }
+RowBodyItem     :       KEYS OBRACE Keys CBRACE SEMI { $$ = NULL; }
+                |       VarDecl
+                        { FreeStmt((ParseCommon *) $1); $$ = NULL; }
                 ;
 
-KeyName                :       KEYNAME         { $$= $1; }
-               ;
+Keys            :       Keys COMMA Key          { $$ = NULL; }
+                |       Key                     { $$ = NULL; }
+                ;
 
-Ident          :       IDENT
+Key             :       KEYNAME
+                        { $$ = NULL; }
+                |       OBRACE ExprList CBRACE
+                        { FreeStmt((ParseCommon *) $2.head); $$ = NULL; }
+                ;
+
+OverlayDecl     :       OVERLAY String OBRACE OverlayKeyList CBRACE SEMI
+                        { $$ = NULL; }
+                ;
+
+OverlayKeyList  :       OverlayKeyList COMMA OverlayKey { $$ = NULL; }
+                |       OverlayKey                      { $$ = NULL; }
+                ;
+
+OverlayKey      :       KEYNAME EQUALS KEYNAME          { $$ = NULL; }
+                ;
+
+OutlineList     :       OutlineList COMMA OutlineInList
+                        { $$ = NULL;}
+                |       OutlineInList
+                        { $$ = NULL; }
+                ;
+
+OutlineInList   :       OBRACE CoordList CBRACE
+                        { (void) $2; $$ = NULL; }
+                |       Ident EQUALS OBRACE CoordList CBRACE
+                        { (void) $4; $$ = NULL; }
+                |       Ident EQUALS Expr
+                        { FreeStmt((ParseCommon *) $3); $$ = NULL; }
+                ;
+
+CoordList       :       CoordList COMMA Coord
+                        { (void) $1; (void) $3; $$ = NULL; }
+                |       Coord
+                        { (void) $1; $$ = NULL; }
+                ;
+
+Coord           :       OBRACKET SignedNumber COMMA SignedNumber CBRACKET
+                        { $$ = NULL; }
+                ;
+
+DoodadDecl      :       DoodadType String OBRACE VarDeclList CBRACE SEMI
+                        { FreeStmt((ParseCommon *) $4.head); $$ = NULL; }
+                ;
+
+DoodadType      :       TEXT    { $$ = 0; }
+                |       OUTLINE { $$ = 0; }
+                |       SOLID   { $$ = 0; }
+                |       LOGO    { $$ = 0; }
+                ;
+
+FieldSpec       :       Ident   { $$ = $1; }
+                |       Element { $$ = $1; }
+                ;
+
+Element         :       ACTION_TOK
+                        { $$ = xkb_atom_intern_literal(param->ctx, "action"); }
+                |       INTERPRET
+                        { $$ = xkb_atom_intern_literal(param->ctx, "interpret"); }
+                |       TYPE
+                        { $$ = xkb_atom_intern_literal(param->ctx, "type"); }
+                |       KEY
+                        { $$ = xkb_atom_intern_literal(param->ctx, "key"); }
+                |       GROUP
+                        { $$ = xkb_atom_intern_literal(param->ctx, "group"); }
+                |       MODIFIER_MAP
+                        {$$ = xkb_atom_intern_literal(param->ctx, "modifier_map");}
+                |       INDICATOR
+                        { $$ = xkb_atom_intern_literal(param->ctx, "indicator"); }
+                |       SHAPE
+                        { $$ = xkb_atom_intern_literal(param->ctx, "shape"); }
+                |       ROW
+                        { $$ = xkb_atom_intern_literal(param->ctx, "row"); }
+                |       SECTION
+                        { $$ = xkb_atom_intern_literal(param->ctx, "section"); }
+                |       TEXT
+                        { $$ = xkb_atom_intern_literal(param->ctx, "text"); }
+                ;
+
+OptMergeMode    :       MergeMode       { $$ = $1; }
+                |                       { $$ = MERGE_DEFAULT; }
+                ;
+
+MergeMode       :       INCLUDE         { $$ = MERGE_DEFAULT; }
+                |       AUGMENT         { $$ = MERGE_AUGMENT; }
+                |       OVERRIDE        { $$ = MERGE_OVERRIDE; }
+                |       REPLACE         { $$ = MERGE_REPLACE; }
+                |       ALTERNATE
+                {
+                    /*
+                     * This used to be MERGE_ALT_FORM. This functionality was
+                     * unused and has been removed.
+                     */
+                    $$ = MERGE_DEFAULT;
+                }
+                ;
+
+OptExprList     :       ExprList        { $$ = $1; }
+                |                       { $$.head = $$.last = NULL; }
+                ;
+
+ExprList        :       ExprList COMMA Expr
+                        { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
+                |       Expr
+                        { $$.head = $$.last = $1; }
+                ;
+
+Expr            :       Expr DIVIDE Expr
+                        { $$ = ExprCreateBinary(EXPR_DIVIDE, $1, $3); }
+                |       Expr PLUS Expr
+                        { $$ = ExprCreateBinary(EXPR_ADD, $1, $3); }
+                |       Expr MINUS Expr
+                        { $$ = ExprCreateBinary(EXPR_SUBTRACT, $1, $3); }
+                |       Expr TIMES Expr
+                        { $$ = ExprCreateBinary(EXPR_MULTIPLY, $1, $3); }
+                |       Lhs EQUALS Expr
+                        { $$ = ExprCreateBinary(EXPR_ASSIGN, $1, $3); }
+                |       Term
+                        { $$ = $1; }
+                ;
+
+Term            :       MINUS Term
+                        { $$ = ExprCreateUnary(EXPR_NEGATE, $2->expr.value_type, $2); }
+                |       PLUS Term
+                        { $$ = ExprCreateUnary(EXPR_UNARY_PLUS, $2->expr.value_type, $2); }
+                |       EXCLAM Term
+                        { $$ = ExprCreateUnary(EXPR_NOT, EXPR_TYPE_BOOLEAN, $2); }
+                |       INVERT Term
+                        { $$ = ExprCreateUnary(EXPR_INVERT, $2->expr.value_type, $2); }
+                |       Lhs
+                        { $$ = $1;  }
+                |       FieldSpec OPAREN OptExprList CPAREN %prec OPAREN
+                        { $$ = ExprCreateAction($1, $3.head); }
+                |       Terminal
+                        { $$ = $1;  }
+                |       OPAREN Expr CPAREN
+                        { $$ = $2;  }
+                ;
+
+ActionList      :       ActionList COMMA Action
+                        { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
+                |       Action
+                        { $$.head = $$.last = $1; }
+                ;
+
+Action          :       FieldSpec OPAREN OptExprList CPAREN
+                        { $$ = ExprCreateAction($1, $3.head); }
+                ;
+
+Lhs             :       FieldSpec
+                        { $$ = ExprCreateIdent($1); }
+                |       FieldSpec DOT FieldSpec
+                        { $$ = ExprCreateFieldRef($1, $3); }
+                |       FieldSpec OBRACKET Expr CBRACKET
+                        { $$ = ExprCreateArrayRef(XKB_ATOM_NONE, $1, $3); }
+                |       FieldSpec DOT FieldSpec OBRACKET Expr CBRACKET
+                        { $$ = ExprCreateArrayRef($1, $3, $5); }
+                ;
+
+Terminal        :       String
+                        { $$ = ExprCreateString($1); }
+                |       Integer
+                        { $$ = ExprCreateInteger($1); }
+                |       Float
+                        { $$ = ExprCreateFloat(/* Discard $1 */); }
+                |       KEYNAME
+                        { $$ = ExprCreateKeyName($1); }
+                ;
+
+OptKeySymList   :       KeySymList      { $$ = $1; }
+                |                       { $$ = NULL; }
+                ;
+
+KeySymList      :       KeySymList COMMA KeySym
+                        { $$ = ExprAppendKeysymList($1, $3); }
+                |       KeySymList COMMA KeySyms
+                        { $$ = ExprAppendMultiKeysymList($1, $3); }
+                |       KeySym
+                        { $$ = ExprCreateKeysymList($1); }
+                |       KeySyms
+                        { $$ = ExprCreateMultiKeysymList($1); }
+                ;
+
+KeySyms         :       OBRACE KeySymList CBRACE
+                        { $$ = $2; }
+                ;
+
+KeySym          :       IDENT
                         {
-                            $$ = xkb_atom_intern(param->ctx, $1);
+                            if (!resolve_keysym($1, &$$)) {
+                                parser_warn(
+                                    param,
+                                    XKB_WARNING_UNRECOGNIZED_KEYSYM,
+                                    "unrecognized keysym \"%s\"",
+                                    $1
+                                );
+                                $$ = XKB_KEY_NoSymbol;
+                            }
                             free($1);
                         }
-               |       DEFAULT
+                |       SECTION { $$ = XKB_KEY_section; }
+                |       Integer
                         {
-                            $$ = xkb_atom_intern(param->ctx, "default");
+                            if ($1 < XKB_KEYSYM_MIN) {
+                                parser_warn(
+                                    param,
+                                    XKB_WARNING_UNRECOGNIZED_KEYSYM,
+                                    "unrecognized keysym \"%"PRId64"\"",
+                                    $1
+                                );
+                                $$ = XKB_KEY_NoSymbol;
+                            }
+                            /* Special case for digits 0..9 */
+                            else if ($1 < 10) {      /* XKB_KEY_0 .. XKB_KEY_9 */
+                                $$ = XKB_KEY_0 + (xkb_keysym_t) $1;
+                            }
+                            else {
+                                if ($1 <= XKB_KEYSYM_MAX) {
+                                    $$ = (xkb_keysym_t) $1;
+                                } else {
+                                    parser_warn(
+                                        param, XKB_WARNING_UNRECOGNIZED_KEYSYM,
+                                        "unrecognized keysym \"0x%"PRIx64"\" "
+                                        "(%"PRId64")", $1, $1
+                                    );
+                                    $$ = XKB_KEY_NoSymbol;
+                                }
+                            }
                         }
-               ;
+                ;
 
-String         :       STRING
-                        {
-                            $$ = xkb_atom_intern(param->ctx, $1);
-                            free($1);
-                        }
-               ;
+SignedNumber    :       MINUS Number    { $$ = -$2; }
+                |       Number          { $$ = $1; }
+                ;
+
+Number          :       FLOAT   { $$ = $1; }
+                |       INTEGER { $$ = $1; }
+                ;
+
+Float           :       FLOAT   { $$ = 0; }
+                ;
 
-OptMapName     :       MapName { $$= $1; }
-               |               { $$= NULL; }
-               ;
+Integer         :       INTEGER { $$ = $1; }
+                ;
+
+KeyCode         :       INTEGER { $$ = $1; }
+                ;
 
-MapName                :       STRING  { $$= $1; }
-               ;
+Ident           :       IDENT   { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); }
+                |       DEFAULT { $$ = xkb_atom_intern_literal(param->ctx, "default"); }
+                ;
+
+String          :       STRING  { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); }
+                ;
+
+OptMapName      :       MapName { $$ = $1; }
+                |               { $$ = NULL; }
+                ;
+
+MapName         :       STRING  { $$ = $1; }
+                ;
 
 %%
 
-#undef scanner
+XkbFile *
+parse(struct xkb_context *ctx, struct scanner *scanner, const char *map)
+{
+    int ret;
+    XkbFile *first = NULL;
+    struct parser_param param = {
+        .scanner = scanner,
+        .ctx = ctx,
+        .rtrn = NULL,
+        .more_maps = false,
+    };
+
+    /*
+     * If we got a specific map, we look for it exclusively and return
+     * immediately upon finding it. Otherwise, we need to get the
+     * default map. If we find a map marked as default, we return it
+     * immediately. If there are no maps marked as default, we return
+     * the first map in the file.
+     */
+
+    while ((ret = yyparse(&param)) == 0 && param.more_maps) {
+        if (map) {
+            if (streq_not_null(map, param.rtrn->name))
+                return param.rtrn;
+            else
+                FreeXkbFile(param.rtrn);
+        }
+        else {
+            if (param.rtrn->flags & MAP_IS_DEFAULT) {
+                FreeXkbFile(first);
+                return param.rtrn;
+            }
+            else if (!first) {
+                first = param.rtrn;
+            }
+            else {
+                FreeXkbFile(param.rtrn);
+            }
+        }
+        param.rtrn = NULL;
+    }
+
+    if (ret != 0) {
+        FreeXkbFile(first);
+        return NULL;
+    }
+
+    if (first)
+        log_vrb(ctx, 5,
+                "No map in include statement, but \"%s\" contains several; "
+                "Using first defined map, \"%s\"\n",
+                scanner->file_name, first->name);
+
+    return first;
+}