1 /************************************************************
2 Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 ********************************************************/
28 * The parser should work with reasonably recent versions of either
29 * bison or byacc. So if you make changes, try to make sure it works
34 #include "xkbcomp/xkbcomp-priv.h"
35 #include "xkbcomp/ast-build.h"
36 #include "xkbcomp/parser-priv.h"
37 #include "scanner-utils.h"
40 struct xkb_context *ctx;
41 struct scanner *scanner;
46 #define parser_err(param, fmt, ...) \
47 scanner_err((param)->scanner, fmt, ##__VA_ARGS__)
49 #define parser_warn(param, fmt, ...) \
50 scanner_warn((param)->scanner, fmt, ##__VA_ARGS__)
53 _xkbcommon_error(struct parser_param *param, const char *msg)
55 parser_err(param, "%s", msg);
59 resolve_keysym(const char *name, xkb_keysym_t *sym_rtrn)
63 if (!name || istreq(name, "any") || istreq(name, "nosymbol")) {
64 *sym_rtrn = XKB_KEY_NoSymbol;
68 if (istreq(name, "none") || istreq(name, "voidsymbol")) {
69 *sym_rtrn = XKB_KEY_VoidSymbol;
73 sym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS);
74 if (sym != XKB_KEY_NoSymbol) {
82 #define param_scanner param->scanner
86 %lex-param { struct scanner *param_scanner }
87 %parse-param { struct parser_param *param }
165 enum xkb_file_type file_type;
168 enum merge_mode merge;
169 enum xkb_map_flags mapFlags;
172 struct { ParseCommon *head; ParseCommon *last; } anyList;
174 struct { ExprDef *head; ExprDef *last; } exprList;
176 struct { VarDef *head; VarDef *last; } varList;
178 struct { VModDef *head; VModDef *last; } vmodList;
183 GroupCompatDef *groupCompat;
187 KeyAliasDef *keyAlias;
190 struct { XkbFile *head; XkbFile *last; } fileList;
193 %type <num> INTEGER FLOAT
194 %type <str> IDENT STRING
197 %type <ival> Number Integer Float SignedNumber DoodadType
198 %type <merge> MergeMode OptMergeMode
199 %type <file_type> XkbCompositeType FileType
200 %type <mapFlags> Flag Flags OptFlags
201 %type <str> MapName OptMapName
202 %type <atom> FieldSpec Ident Element String
203 %type <keysym> KeySym
205 %type <anyList> DeclList
206 %type <expr> Expr Term Lhs Terminal ArrayInit KeySyms
207 %type <expr> OptKeySymList KeySymList Action Coord CoordList
208 %type <exprList> OptExprList ExprList ActionList
209 %type <var> VarDecl SymbolsVarDecl
210 %type <varList> VarDeclList SymbolsBody
212 %type <vmodList> VModDefList VModDecl
213 %type <interp> InterpretDecl InterpretMatch
214 %type <keyType> KeyTypeDecl
215 %type <syms> SymbolsDecl
216 %type <modMask> ModMapDecl
217 %type <groupCompat> GroupCompatDecl
218 %type <ledMap> LedMapDecl
219 %type <ledName> LedNameDecl
220 %type <keyCode> KeyNameDecl
221 %type <keyAlias> KeyAliasDecl
222 %type <geom> ShapeDecl SectionDecl SectionBody SectionBodyItem RowBody RowBodyItem
223 %type <geom> Keys Key OverlayDecl OverlayKeyList OverlayKey OutlineList OutlineInList
224 %type <geom> DoodadDecl
225 %type <file> XkbFile XkbMapConfig
226 %type <fileList> XkbMapConfigList
227 %type <file> XkbCompositeMap
229 %destructor { FreeStmt((ParseCommon *) $$); }
230 <any> <expr> <var> <vmod> <interp> <keyType> <syms> <modMask> <groupCompat>
231 <ledMap> <ledName> <keyCode> <keyAlias>
232 %destructor { FreeStmt((ParseCommon *) $$.head); }
233 <anyList> <exprList> <varList> <vmodList>
234 /* The destructor also runs on the start symbol when the parser *succeeds*.
235 * The `if` here catches this case. */
236 %destructor { if (!param->rtrn) FreeXkbFile($$); } <file>
237 %destructor { FreeXkbFile($$.head); } <fileList>
238 %destructor { free($$); } <str>
243 * An actual file may contain more than one map. However, if we do things
244 * in the normal yacc way, i.e. aggregate all of the maps into a list and
245 * let the caller find the map it wants, we end up scanning and parsing a
246 * lot of unneeded maps (in the end we always just need one).
247 * Instead of doing that, we make yyparse return one map at a time, and
248 * then call it repeatedly until we find the map we need. Once we find it,
249 * we don't need to parse everything that follows in the file.
250 * This does mean that if we e.g. always use the first map, the file may
251 * contain complete garbage after that. But it's worth it.
254 XkbFile : XkbCompositeMap
255 { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; }
257 { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; YYACCEPT; }
259 { $$ = param->rtrn = NULL; param->more_maps = false; }
262 XkbCompositeMap : OptFlags XkbCompositeType OptMapName OBRACE
265 { $$ = XkbFileCreate($2, $3, (ParseCommon *) $5.head, $1); }
268 XkbCompositeType: XKB_KEYMAP { $$ = FILE_TYPE_KEYMAP; }
269 | XKB_SEMANTICS { $$ = FILE_TYPE_KEYMAP; }
270 | XKB_LAYOUT { $$ = FILE_TYPE_KEYMAP; }
273 XkbMapConfigList : XkbMapConfigList XkbMapConfig
274 { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; }
276 { $$.head = $$.last = $1; }
279 XkbMapConfig : OptFlags FileType OptMapName OBRACE
283 $$ = XkbFileCreate($2, $3, $5.head, $1);
287 FileType : XKB_KEYCODES { $$ = FILE_TYPE_KEYCODES; }
288 | XKB_TYPES { $$ = FILE_TYPE_TYPES; }
289 | XKB_COMPATMAP { $$ = FILE_TYPE_COMPAT; }
290 | XKB_SYMBOLS { $$ = FILE_TYPE_SYMBOLS; }
291 | XKB_GEOMETRY { $$ = FILE_TYPE_GEOMETRY; }
294 OptFlags : Flags { $$ = $1; }
298 Flags : Flags Flag { $$ = ($1 | $2); }
302 Flag : PARTIAL { $$ = MAP_IS_PARTIAL; }
303 | DEFAULT { $$ = MAP_IS_DEFAULT; }
304 | HIDDEN { $$ = MAP_IS_HIDDEN; }
305 | ALPHANUMERIC_KEYS { $$ = MAP_HAS_ALPHANUMERIC; }
306 | MODIFIER_KEYS { $$ = MAP_HAS_MODIFIER; }
307 | KEYPAD_KEYS { $$ = MAP_HAS_KEYPAD; }
308 | FUNCTION_KEYS { $$ = MAP_HAS_FN; }
309 | ALTERNATE_GROUP { $$ = MAP_IS_ALTGR; }
312 DeclList : DeclList Decl
315 * TODO: This is needed because of VModDecl, which
316 * is a list and is "inlined" into the DeclList.
317 * Should change to avoid the O(N)-append behavior,
318 * like we do in the other lists.
328 $$.last = $1.last->next = $2;
329 while ($$.last->next)
330 $$.last = $$.last->next;
333 | { $$.head = $$.last = NULL; }
336 Decl : OptMergeMode VarDecl
339 $$ = (ParseCommon *) $2;
341 | OptMergeMode VModDecl
343 for (VModDef *vmod = $2.head; vmod; vmod = (VModDef *) vmod->common.next)
345 $$ = (ParseCommon *) $2.head;
347 | OptMergeMode InterpretDecl
350 $$ = (ParseCommon *) $2;
352 | OptMergeMode KeyNameDecl
355 $$ = (ParseCommon *) $2;
357 | OptMergeMode KeyAliasDecl
360 $$ = (ParseCommon *) $2;
362 | OptMergeMode KeyTypeDecl
365 $$ = (ParseCommon *) $2;
367 | OptMergeMode SymbolsDecl
370 $$ = (ParseCommon *) $2;
372 | OptMergeMode ModMapDecl
375 $$ = (ParseCommon *) $2;
377 | OptMergeMode GroupCompatDecl
380 $$ = (ParseCommon *) $2;
382 | OptMergeMode LedMapDecl
385 $$ = (ParseCommon *) $2;
387 | OptMergeMode LedNameDecl
390 $$ = (ParseCommon *) $2;
392 | OptMergeMode ShapeDecl { $$ = NULL; }
393 | OptMergeMode SectionDecl { $$ = NULL; }
394 | OptMergeMode DoodadDecl { $$ = NULL; }
397 $$ = (ParseCommon *) IncludeCreate(param->ctx, $2, $1);
402 VarDecl : Lhs EQUALS Expr SEMI
403 { $$ = VarCreate($1, $3); }
405 { $$ = BoolVarCreate($1, true); }
407 { $$ = BoolVarCreate($2, false); }
410 KeyNameDecl : KEYNAME EQUALS KeyCode SEMI
411 { $$ = KeycodeCreate($1, $3); }
414 KeyAliasDecl : ALIAS KEYNAME EQUALS KEYNAME SEMI
415 { $$ = KeyAliasCreate($2, $4); }
418 VModDecl : VIRTUAL_MODS VModDefList SEMI
422 VModDefList : VModDefList COMMA VModDef
423 { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
425 { $$.head = $$.last = $1; }
429 { $$ = VModCreate($1, NULL); }
431 { $$ = VModCreate($1, $3); }
434 InterpretDecl : INTERPRET InterpretMatch OBRACE
437 { $2->def = $4.head; $$ = $2; }
440 InterpretMatch : KeySym PLUS Expr
441 { $$ = InterpCreate($1, $3); }
443 { $$ = InterpCreate($1, NULL); }
446 VarDeclList : VarDeclList VarDecl
447 { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; }
449 { $$.head = $$.last = $1; }
452 KeyTypeDecl : TYPE String OBRACE
455 { $$ = KeyTypeCreate($2, $4.head); }
458 SymbolsDecl : KEY KEYNAME OBRACE
461 { $$ = SymbolsCreate($2, $4.head); }
464 SymbolsBody : SymbolsBody COMMA SymbolsVarDecl
465 { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
467 { $$.head = $$.last = $1; }
468 | { $$.head = $$.last = NULL; }
471 SymbolsVarDecl : Lhs EQUALS Expr { $$ = VarCreate($1, $3); }
472 | Lhs EQUALS ArrayInit { $$ = VarCreate($1, $3); }
473 | Ident { $$ = BoolVarCreate($1, true); }
474 | EXCLAM Ident { $$ = BoolVarCreate($2, false); }
475 | ArrayInit { $$ = VarCreate(NULL, $1); }
478 ArrayInit : OBRACKET OptKeySymList CBRACKET
480 | OBRACKET ActionList CBRACKET
481 { $$ = ExprCreateActionList($2.head); }
484 GroupCompatDecl : GROUP Integer EQUALS Expr SEMI
485 { $$ = GroupCompatCreate($2, $4); }
488 ModMapDecl : MODIFIER_MAP Ident OBRACE ExprList CBRACE SEMI
489 { $$ = ModMapCreate($2, $4.head); }
492 LedMapDecl: INDICATOR String OBRACE VarDeclList CBRACE SEMI
493 { $$ = LedMapCreate($2, $4.head); }
496 LedNameDecl: INDICATOR Integer EQUALS Expr SEMI
497 { $$ = LedNameCreate($2, $4, false); }
498 | VIRTUAL INDICATOR Integer EQUALS Expr SEMI
499 { $$ = LedNameCreate($3, $5, true); }
502 ShapeDecl : SHAPE String OBRACE OutlineList CBRACE SEMI
504 | SHAPE String OBRACE CoordList CBRACE SEMI
505 { (void) $4; $$ = NULL; }
508 SectionDecl : SECTION String OBRACE SectionBody CBRACE SEMI
512 SectionBody : SectionBody SectionBodyItem { $$ = NULL;}
513 | SectionBodyItem { $$ = NULL; }
516 SectionBodyItem : ROW OBRACE RowBody CBRACE SEMI
519 { FreeStmt((ParseCommon *) $1); $$ = NULL; }
523 { FreeStmt((ParseCommon *) $1); $$ = NULL; }
528 RowBody : RowBody RowBodyItem { $$ = NULL;}
529 | RowBodyItem { $$ = NULL; }
532 RowBodyItem : KEYS OBRACE Keys CBRACE SEMI { $$ = NULL; }
534 { FreeStmt((ParseCommon *) $1); $$ = NULL; }
537 Keys : Keys COMMA Key { $$ = NULL; }
543 | OBRACE ExprList CBRACE
544 { FreeStmt((ParseCommon *) $2.head); $$ = NULL; }
547 OverlayDecl : OVERLAY String OBRACE OverlayKeyList CBRACE SEMI
551 OverlayKeyList : OverlayKeyList COMMA OverlayKey { $$ = NULL; }
552 | OverlayKey { $$ = NULL; }
555 OverlayKey : KEYNAME EQUALS KEYNAME { $$ = NULL; }
558 OutlineList : OutlineList COMMA OutlineInList
564 OutlineInList : OBRACE CoordList CBRACE
565 { (void) $2; $$ = NULL; }
566 | Ident EQUALS OBRACE CoordList CBRACE
567 { (void) $4; $$ = NULL; }
569 { FreeStmt((ParseCommon *) $3); $$ = NULL; }
572 CoordList : CoordList COMMA Coord
573 { (void) $1; (void) $3; $$ = NULL; }
575 { (void) $1; $$ = NULL; }
578 Coord : OBRACKET SignedNumber COMMA SignedNumber CBRACKET
582 DoodadDecl : DoodadType String OBRACE VarDeclList CBRACE SEMI
583 { FreeStmt((ParseCommon *) $4.head); $$ = NULL; }
586 DoodadType : TEXT { $$ = 0; }
587 | OUTLINE { $$ = 0; }
592 FieldSpec : Ident { $$ = $1; }
593 | Element { $$ = $1; }
597 { $$ = xkb_atom_intern_literal(param->ctx, "action"); }
599 { $$ = xkb_atom_intern_literal(param->ctx, "interpret"); }
601 { $$ = xkb_atom_intern_literal(param->ctx, "type"); }
603 { $$ = xkb_atom_intern_literal(param->ctx, "key"); }
605 { $$ = xkb_atom_intern_literal(param->ctx, "group"); }
607 {$$ = xkb_atom_intern_literal(param->ctx, "modifier_map");}
609 { $$ = xkb_atom_intern_literal(param->ctx, "indicator"); }
611 { $$ = xkb_atom_intern_literal(param->ctx, "shape"); }
613 { $$ = xkb_atom_intern_literal(param->ctx, "row"); }
615 { $$ = xkb_atom_intern_literal(param->ctx, "section"); }
617 { $$ = xkb_atom_intern_literal(param->ctx, "text"); }
620 OptMergeMode : MergeMode { $$ = $1; }
621 | { $$ = MERGE_DEFAULT; }
624 MergeMode : INCLUDE { $$ = MERGE_DEFAULT; }
625 | AUGMENT { $$ = MERGE_AUGMENT; }
626 | OVERRIDE { $$ = MERGE_OVERRIDE; }
627 | REPLACE { $$ = MERGE_REPLACE; }
631 * This used to be MERGE_ALT_FORM. This functionality was
632 * unused and has been removed.
638 OptExprList : ExprList { $$ = $1; }
639 | { $$.head = $$.last = NULL; }
642 ExprList : ExprList COMMA Expr
643 { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
645 { $$.head = $$.last = $1; }
648 Expr : Expr DIVIDE Expr
649 { $$ = ExprCreateBinary(EXPR_DIVIDE, $1, $3); }
651 { $$ = ExprCreateBinary(EXPR_ADD, $1, $3); }
653 { $$ = ExprCreateBinary(EXPR_SUBTRACT, $1, $3); }
655 { $$ = ExprCreateBinary(EXPR_MULTIPLY, $1, $3); }
657 { $$ = ExprCreateBinary(EXPR_ASSIGN, $1, $3); }
663 { $$ = ExprCreateUnary(EXPR_NEGATE, $2->expr.value_type, $2); }
665 { $$ = ExprCreateUnary(EXPR_UNARY_PLUS, $2->expr.value_type, $2); }
667 { $$ = ExprCreateUnary(EXPR_NOT, EXPR_TYPE_BOOLEAN, $2); }
669 { $$ = ExprCreateUnary(EXPR_INVERT, $2->expr.value_type, $2); }
672 | FieldSpec OPAREN OptExprList CPAREN %prec OPAREN
673 { $$ = ExprCreateAction($1, $3.head); }
680 ActionList : ActionList COMMA Action
681 { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; }
683 { $$.head = $$.last = $1; }
686 Action : FieldSpec OPAREN OptExprList CPAREN
687 { $$ = ExprCreateAction($1, $3.head); }
691 { $$ = ExprCreateIdent($1); }
692 | FieldSpec DOT FieldSpec
693 { $$ = ExprCreateFieldRef($1, $3); }
694 | FieldSpec OBRACKET Expr CBRACKET
695 { $$ = ExprCreateArrayRef(XKB_ATOM_NONE, $1, $3); }
696 | FieldSpec DOT FieldSpec OBRACKET Expr CBRACKET
697 { $$ = ExprCreateArrayRef($1, $3, $5); }
701 { $$ = ExprCreateString($1); }
703 { $$ = ExprCreateInteger($1); }
705 { $$ = ExprCreateFloat(/* Discard $1 */); }
707 { $$ = ExprCreateKeyName($1); }
710 OptKeySymList : KeySymList { $$ = $1; }
714 KeySymList : KeySymList COMMA KeySym
715 { $$ = ExprAppendKeysymList($1, $3); }
716 | KeySymList COMMA KeySyms
717 { $$ = ExprAppendMultiKeysymList($1, $3); }
719 { $$ = ExprCreateKeysymList($1); }
721 { $$ = ExprCreateMultiKeysymList($1); }
724 KeySyms : OBRACE KeySymList CBRACE
730 if (!resolve_keysym($1, &$$))
731 parser_warn(param, "unrecognized keysym \"%s\"", $1);
734 | SECTION { $$ = XKB_KEY_section; }
738 parser_warn(param, "unrecognized keysym \"%d\"", $1);
739 $$ = XKB_KEY_NoSymbol;
741 else if ($1 < 10) { /* XKB_KEY_0 .. XKB_KEY_9 */
742 $$ = XKB_KEY_0 + (xkb_keysym_t) $1;
746 snprintf(buf, sizeof(buf), "0x%x", $1);
747 if (!resolve_keysym(buf, &$$)) {
748 parser_warn(param, "unrecognized keysym \"%s\"", buf);
749 $$ = XKB_KEY_NoSymbol;
755 SignedNumber : MINUS Number { $$ = -$2; }
756 | Number { $$ = $1; }
759 Number : FLOAT { $$ = $1; }
760 | INTEGER { $$ = $1; }
763 Float : FLOAT { $$ = 0; }
766 Integer : INTEGER { $$ = $1; }
769 KeyCode : INTEGER { $$ = $1; }
772 Ident : IDENT { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); }
773 | DEFAULT { $$ = xkb_atom_intern_literal(param->ctx, "default"); }
776 String : STRING { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); }
779 OptMapName : MapName { $$ = $1; }
783 MapName : STRING { $$ = $1; }
789 parse(struct xkb_context *ctx, struct scanner *scanner, const char *map)
792 XkbFile *first = NULL;
793 struct parser_param param = {
801 * If we got a specific map, we look for it exclusively and return
802 * immediately upon finding it. Otherwise, we need to get the
803 * default map. If we find a map marked as default, we return it
804 * immediately. If there are no maps marked as default, we return
805 * the first map in the file.
808 while ((ret = yyparse(¶m)) == 0 && param.more_maps) {
810 if (streq_not_null(map, param.rtrn->name))
813 FreeXkbFile(param.rtrn);
816 if (param.rtrn->flags & MAP_IS_DEFAULT) {
824 FreeXkbFile(param.rtrn);
837 "No map in include statement, but \"%s\" contains several; "
838 "Using first defined map, \"%s\"\n",
839 scanner->file_name, first->name);