From a392d2682bfbf5ce6c3ee153c6a08bb456da0660 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 12 Aug 2012 11:40:02 +0300 Subject: Replace flex scanner with a hand-written one The scanner is very similar in structure to the one in xkbcomp/rules.c. It avoids copying and has nicer error reporting. It uses gperf to generate a hashtable for the keywords, which gives a nice speed boost (compared to the naive strcasecmp method at least). But since there's hardly a reason to regenerate it every time and require people to install gperf, the output (keywords.c) is added here as well. Here are some stats from test/rulescomp: Before: compiled 1000 keymaps in 4.052939625s ==22063== total heap usage: 101,101 allocs, 101,101 frees, 11,840,834 bytes allocated After: compiled 1000 keymaps in 3.519665434s ==26505== total heap usage: 99,945 allocs, 99,945 frees, 7,033,608 bytes allocated Signed-off-by: Ran Benita --- Makefile.am | 13 +- configure.ac | 2 +- src/keymap.c | 32 +---- src/keymap.h | 4 +- src/xkbcomp/.gitignore | 1 - src/xkbcomp/keywords.c | 349 +++++++++++++++++++++++++++++++++++++++++++++ src/xkbcomp/keywords.gperf | 79 ++++++++++ src/xkbcomp/parser-priv.h | 14 +- src/xkbcomp/parser.y | 2 +- src/xkbcomp/scanner.c | 205 ++++++++++++++++++++++++++ src/xkbcomp/scanner.l | 323 ----------------------------------------- src/xkbcomp/xkbcomp-priv.h | 9 +- src/xkbcomp/xkbcomp.c | 38 +---- 13 files changed, 659 insertions(+), 412 deletions(-) create mode 100644 src/xkbcomp/keywords.c create mode 100644 src/xkbcomp/keywords.gperf create mode 100644 src/xkbcomp/scanner.c delete mode 100644 src/xkbcomp/scanner.l diff --git a/Makefile.am b/Makefile.am index 0d43f39..98cabc1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,11 +38,12 @@ libxkbcommon_la_SOURCES = \ src/xkbcomp/keycodes.c \ src/xkbcomp/keymap.c \ src/xkbcomp/keymap-dump.c \ + src/xkbcomp/keywords.c \ src/xkbcomp/parser.y \ src/xkbcomp/parser-priv.h \ src/xkbcomp/rules.c \ src/xkbcomp/rules.h \ - src/xkbcomp/scanner.l \ + src/xkbcomp/scanner.c \ src/xkbcomp/scanner-utils.h \ src/xkbcomp/symbols.c \ src/xkbcomp/types.c \ @@ -70,13 +71,11 @@ libxkbcommon_la_SOURCES = \ BUILT_SOURCES = \ src/xkbcomp/parser.c \ - src/xkbcomp/parser.h \ - src/xkbcomp/scanner.c + src/xkbcomp/parser.h CLEANFILES = $(BUILT_SOURCES) src/xkbcomp/parser.c: $(top_builddir)/src/$(am__dirstamp) $(top_builddir)/src/xkbcomp/$(am__dirstamp) src/xkbcomp/parser.h: $(top_builddir)/src/$(am__dirstamp) $(top_builddir)/src/xkbcomp/$(am__dirstamp) -src/xkbcomp/scanner.c: $(top_builddir)/src/$(am__dirstamp) $(top_builddir)/src/xkbcomp/$(am__dirstamp) # Documentation @@ -171,7 +170,6 @@ EXTRA_DIST = \ # removes #define _OSF_Keysyms and such. The XK_Ydiaeresis case is to # handle a duplicate definition in HPkeysyms.h which kicks in if it's # not already defined. - X11_INCLUDEDIR = /usr/include/X11 KEYSYMDEFS = \ $(X11_INCLUDEDIR)/keysymdef.h \ @@ -191,6 +189,11 @@ update-keysyms: echo -en '/* This file is autogenerated from Makefile.am; please do not commit directly. */\n\n' > $(top_srcdir)/src/ks_tables.h LC_CTYPE=C python $(top_srcdir)/makekeys.py $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h >> $(top_srcdir)/src/ks_tables.h +# Run this if you add/remove a new keyword to the xkbcomp scanner, +# or just want to regenerate the gperf file. +update-keywords: + $(AM_V_GEN)gperf < $(top_srcdir)/src/xkbcomp/keywords.gperf > $(top_srcdir)/src/xkbcomp/keywords.c + # Android stuff Android_build.mk: Makefile $(BUILT_SOURCES) diff --git a/configure.ac b/configure.ac index ec829cd..2806a2c 100644 --- a/configure.ac +++ b/configure.ac @@ -54,7 +54,7 @@ AC_C_INLINE # Check for programs AC_PROG_MKDIR_P PKG_PROG_PKG_CONFIG -AC_PROG_LEX + AC_PROG_YACC AC_PATH_PROG([YACC_INST], $YACC) if test ! -f "src/xkbcomp/parser.c"; then diff --git a/src/keymap.c b/src/keymap.c index 3df183a..55000f4 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -190,35 +190,7 @@ xkb_keymap_new_from_string(struct xkb_context *ctx, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags) { - struct xkb_keymap *keymap; - const struct xkb_keymap_format_ops *ops; - - ops = get_keymap_format_ops(format); - if (!ops || !ops->keymap_new_from_string) { - log_err_func(ctx, "unsupported keymap format: %d\n", format); - return NULL; - } - - if (flags & ~(XKB_MAP_COMPILE_PLACEHOLDER)) { - log_err_func(ctx, "unrecognized flags: %#x\n", flags); - return NULL; - } - - if (!string) { - log_err_func1(ctx, "no string specified\n"); - return NULL; - } - - keymap = xkb_keymap_new(ctx, format, flags); - if (!keymap) - return NULL; - - if (!ops->keymap_new_from_string(keymap, string)) { - xkb_keymap_unref(keymap); - return NULL; - } - - return keymap; + return xkb_keymap_new_from_buffer(ctx, string, SIZE_MAX, format, flags); } XKB_EXPORT struct xkb_keymap * @@ -250,7 +222,7 @@ xkb_keymap_new_from_buffer(struct xkb_context *ctx, if (!keymap) return NULL; - if (!ops->keymap_new_from_buffer(keymap, buffer, length)) { + if (!ops->keymap_new_from_string(keymap, buffer, length)) { xkb_keymap_unref(keymap); return NULL; } diff --git a/src/keymap.h b/src/keymap.h index c6b884d..7d51184 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -439,9 +439,7 @@ struct xkb_keymap_format_ops { bool (*keymap_new_from_names)(struct xkb_keymap *keymap, const struct xkb_rule_names *names); bool (*keymap_new_from_string)(struct xkb_keymap *keymap, - const char *string); - bool (*keymap_new_from_buffer)(struct xkb_keymap *keymap, - const char *buffer, size_t length); + const char *string, size_t length); bool (*keymap_new_from_file)(struct xkb_keymap *keymap, FILE *file); char *(*keymap_get_as_string)(struct xkb_keymap *keymap); }; diff --git a/src/xkbcomp/.gitignore b/src/xkbcomp/.gitignore index 2081535..d7814e4 100644 --- a/src/xkbcomp/.gitignore +++ b/src/xkbcomp/.gitignore @@ -1,3 +1,2 @@ parser.c parser.h -scanner.c diff --git a/src/xkbcomp/keywords.c b/src/xkbcomp/keywords.c new file mode 100644 index 0000000..dba7086 --- /dev/null +++ b/src/xkbcomp/keywords.c @@ -0,0 +1,349 @@ +/* ANSI-C code produced by gperf version 3.0.4 */ +/* Command-line: gperf */ +/* Computed positions: -k'1-2,5' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + + +#include "xkbcomp-priv.h" +#include "parser-priv.h" + +static unsigned int +keyword_gperf_hash(const char *str, unsigned int len); + +static const struct keyword_tok * +keyword_gperf_lookup(const char *str, unsigned int len); +struct keyword_tok { int name; enum yytokentype tok; }; +#include +/* maximum key range = 70, duplicates = 0 */ + +#ifndef GPERF_DOWNCASE +#define GPERF_DOWNCASE 1 +static unsigned char gperf_downcase[256] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 + }; +#endif + +#ifndef GPERF_CASE_STRCMP +#define GPERF_CASE_STRCMP 1 +static int +gperf_case_strcmp (register const char *s1, register const char *s2) +{ + for (;;) + { + unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; + unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; + if (c1 != 0 && c1 == c2) + continue; + return (int)c1 - (int)c2; + } +} +#endif + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +keyword_gperf_hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 0, 73, 5, 36, 0, + 10, 1, 15, 15, 73, 0, 10, 20, 35, 20, + 50, 73, 10, 10, 5, 0, 15, 73, 0, 15, + 73, 73, 73, 73, 73, 73, 73, 0, 73, 5, + 36, 0, 10, 1, 15, 15, 73, 0, 10, 20, + 35, 20, 50, 73, 10, 10, 5, 0, 15, 73, + 0, 15, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + case 3: + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +struct stringpool_t + { + char stringpool_str3[sizeof("key")]; + char stringpool_str4[sizeof("keys")]; + char stringpool_str7[sizeof("augment")]; + char stringpool_str9[sizeof("text")]; + char stringpool_str10[sizeof("xkb_keymap")]; + char stringpool_str11[sizeof("keypad_keys")]; + char stringpool_str12[sizeof("xkb_keycodes")]; + char stringpool_str13[sizeof("xkb_geometry")]; + char stringpool_str14[sizeof("xkb_types")]; + char stringpool_str15[sizeof("xkb_compat")]; + char stringpool_str17[sizeof("replace")]; + char stringpool_str19[sizeof("xkb_compat_map")]; + char stringpool_str20[sizeof("xkb_layout")]; + char stringpool_str21[sizeof("xkb_symbols")]; + char stringpool_str22[sizeof("xkb_compatibility")]; + char stringpool_str23[sizeof("xkb_semantics")]; + char stringpool_str24[sizeof("type")]; + char stringpool_str25[sizeof("alias")]; + char stringpool_str26[sizeof("xkb_compatibility_map")]; + char stringpool_str27[sizeof("alphanumeric_keys")]; + char stringpool_str28[sizeof("function_keys")]; + char stringpool_str29[sizeof("alternate")]; + char stringpool_str30[sizeof("shape")]; + char stringpool_str31[sizeof("action")]; + char stringpool_str32[sizeof("section")]; + char stringpool_str33[sizeof("row")]; + char stringpool_str34[sizeof("logo")]; + char stringpool_str35[sizeof("alternate_group")]; + char stringpool_str36[sizeof("hidden")]; + char stringpool_str37[sizeof("virtual")]; + char stringpool_str42[sizeof("outline")]; + char stringpool_str43[sizeof("default")]; + char stringpool_str46[sizeof("modmap")]; + char stringpool_str47[sizeof("virtual_modifiers")]; + char stringpool_str52[sizeof("overlay")]; + char stringpool_str53[sizeof("override")]; + char stringpool_str57[sizeof("include")]; + char stringpool_str62[sizeof("modifier_map")]; + char stringpool_str63[sizeof("modifier_keys")]; + char stringpool_str64[sizeof("indicator")]; + char stringpool_str66[sizeof("group")]; + char stringpool_str67[sizeof("mod_map")]; + char stringpool_str69[sizeof("interpret")]; + char stringpool_str71[sizeof("solid")]; + char stringpool_str72[sizeof("partial")]; + }; +static const struct stringpool_t stringpool_contents = + { + "key", + "keys", + "augment", + "text", + "xkb_keymap", + "keypad_keys", + "xkb_keycodes", + "xkb_geometry", + "xkb_types", + "xkb_compat", + "replace", + "xkb_compat_map", + "xkb_layout", + "xkb_symbols", + "xkb_compatibility", + "xkb_semantics", + "type", + "alias", + "xkb_compatibility_map", + "alphanumeric_keys", + "function_keys", + "alternate", + "shape", + "action", + "section", + "row", + "logo", + "alternate_group", + "hidden", + "virtual", + "outline", + "default", + "modmap", + "virtual_modifiers", + "overlay", + "override", + "include", + "modifier_map", + "modifier_keys", + "indicator", + "group", + "mod_map", + "interpret", + "solid", + "partial" + }; +#define stringpool ((const char *) &stringpool_contents) +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 45, + MIN_WORD_LENGTH = 3, + MAX_WORD_LENGTH = 21, + MIN_HASH_VALUE = 3, + MAX_HASH_VALUE = 72 + }; + + static const struct keyword_tok wordlist[] = + { + {-1}, {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str3, KEY}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str4, KEYS}, + {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str7, AUGMENT}, + {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str9, TEXT}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str10, XKB_KEYMAP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str11, KEYPAD_KEYS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str12, XKB_KEYCODES}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str13, XKB_GEOMETRY}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str14, XKB_TYPES}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str15, XKB_COMPATMAP}, + {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str17, REPLACE}, + {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str19, XKB_COMPATMAP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str20, XKB_LAYOUT}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str21, XKB_SYMBOLS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str22, XKB_COMPATMAP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str23, XKB_SEMANTICS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str24, TYPE}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str25, ALIAS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str26, XKB_COMPATMAP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str27, ALPHANUMERIC_KEYS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str28, FUNCTION_KEYS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str29, ALTERNATE}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str30, SHAPE}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str31, ACTION_TOK}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str32, SECTION}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str33, ROW}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str34, LOGO}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str35, ALTERNATE_GROUP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str36, HIDDEN}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str37, VIRTUAL}, + {-1}, {-1}, {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str42, OUTLINE}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str43, DEFAULT}, + {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str46, MODIFIER_MAP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str47, VIRTUAL_MODS}, + {-1}, {-1}, {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str52, OVERLAY}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str53, OVERRIDE}, + {-1}, {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str57, INCLUDE}, + {-1}, {-1}, {-1}, {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str62, MODIFIER_MAP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str63, MODIFIER_KEYS}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str64, INDICATOR}, + {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str66, GROUP}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str67, MODIFIER_MAP}, + {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str69, INTERPRET}, + {-1}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str71, SOLID}, + {(int)(long)&((struct stringpool_t *)0)->stringpool_str72, PARTIAL} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = keyword_gperf_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register int o = wordlist[key].name; + if (o >= 0) + { + register const char *s = o + stringpool; + + if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strcmp (str, s)) + return &wordlist[key]; + } + } + } + return 0; +} + + +int +keyword_to_token(const char *string) +{ + const struct keyword_tok *kt; + kt = keyword_gperf_lookup(string, strlen(string)); + if (!kt) + return -1; + return kt->tok; +} diff --git a/src/xkbcomp/keywords.gperf b/src/xkbcomp/keywords.gperf new file mode 100644 index 0000000..cfbfe8f --- /dev/null +++ b/src/xkbcomp/keywords.gperf @@ -0,0 +1,79 @@ +%{ +#include "xkbcomp-priv.h" +#include "parser-priv.h" + +static unsigned int +keyword_gperf_hash(const char *str, unsigned int len); + +static const struct keyword_tok * +keyword_gperf_lookup(const char *str, unsigned int len); +%} + +struct keyword_tok { int name; enum yytokentype tok; }; +%language=ANSI-C +%define hash-function-name keyword_gperf_hash +%define lookup-function-name keyword_gperf_lookup +%readonly-tables +%enum +%includes +%struct-type +%pic +%ignore-case + +%% +action, ACTION_TOK +alias, ALIAS +alphanumeric_keys, ALPHANUMERIC_KEYS +alternate_group, ALTERNATE_GROUP +alternate, ALTERNATE +augment, AUGMENT +default, DEFAULT +function_keys, FUNCTION_KEYS +group, GROUP +hidden, HIDDEN +include, INCLUDE +indicator, INDICATOR +interpret, INTERPRET +keypad_keys, KEYPAD_KEYS +key, KEY +keys, KEYS +logo, LOGO +modifier_keys, MODIFIER_KEYS +modifier_map, MODIFIER_MAP +mod_map, MODIFIER_MAP +modmap, MODIFIER_MAP +outline, OUTLINE +overlay, OVERLAY +override, OVERRIDE +partial, PARTIAL +replace, REPLACE +row, ROW +section, SECTION +shape, SHAPE +solid, SOLID +text, TEXT +type, TYPE +virtual_modifiers, VIRTUAL_MODS +virtual, VIRTUAL +xkb_compatibility_map, XKB_COMPATMAP +xkb_compatibility, XKB_COMPATMAP +xkb_compat_map, XKB_COMPATMAP +xkb_compat, XKB_COMPATMAP +xkb_geometry, XKB_GEOMETRY +xkb_keycodes, XKB_KEYCODES +xkb_keymap, XKB_KEYMAP +xkb_layout, XKB_LAYOUT +xkb_semantics, XKB_SEMANTICS +xkb_symbols, XKB_SYMBOLS +xkb_types, XKB_TYPES +%% + +int +keyword_to_token(const char *string) +{ + const struct keyword_tok *kt; + kt = keyword_gperf_lookup(string, strlen(string)); + if (!kt) + return -1; + return kt->tok; +} diff --git a/src/xkbcomp/parser-priv.h b/src/xkbcomp/parser-priv.h index 2e02db6..465b19d 100644 --- a/src/xkbcomp/parser-priv.h +++ b/src/xkbcomp/parser-priv.h @@ -27,21 +27,21 @@ #ifndef XKBCOMP_PARSER_PRIV_H #define XKBCOMP_PARSER_PRIV_H -struct scanner_extra; +struct scanner; struct parser_param; -#pragma GCC diagnostic ignored "-Wredundant-decls" -#pragma GCC diagnostic push #include "parser.h" -#pragma GCC diagnostic pop -void -scanner_error(YYLTYPE *loc, void *scanner, const char *msg); +int +scanner_error(YYLTYPE *yylloc, struct scanner *scanner, const char *msg); int -_xkbcommon_lex(YYSTYPE *val, YYLTYPE *loc, void *scanner); +_xkbcommon_lex(YYSTYPE *yylval, YYLTYPE *yylloc, struct scanner *scanner); XkbFile * parse(struct xkb_context *ctx, void *scanner, const char *map); +int +keyword_to_token(const char *string); + #endif diff --git a/src/xkbcomp/parser.y b/src/xkbcomp/parser.y index 90ac75e..aaf87db 100644 --- a/src/xkbcomp/parser.y +++ b/src/xkbcomp/parser.y @@ -48,7 +48,7 @@ _xkbcommon_error(struct YYLTYPE *loc, struct parser_param *param, const char *ms %name-prefix "_xkbcommon_" %define api.pure %locations -%lex-param { void *scanner } +%lex-param { struct scanner *scanner } %parse-param { struct parser_param *param } %token diff --git a/src/xkbcomp/scanner.c b/src/xkbcomp/scanner.c new file mode 100644 index 0000000..4be2c40 --- /dev/null +++ b/src/xkbcomp/scanner.c @@ -0,0 +1,205 @@ +/* + * Copyright © 2012 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "xkbcomp-priv.h" +#include "parser-priv.h" +#include "scanner-utils.h" + +int +scanner_error(YYLTYPE *yylloc, struct scanner *s, const char *msg) +{ + log_err(s->ctx, "%s:%d:%d: %s\n", + s->file_name, yylloc->first_line, yylloc->first_column, msg); + return ERROR_TOK; +} + +static bool +number(struct scanner *s, int64_t *out, enum yytokentype *out_tok) +{ + bool is_float = false, is_hex = false; + const char *start = s->s + s->pos; + char *end; + + if (lit(s, "0x")) { + while (isxdigit(peek(s))) next(s); + is_hex = true; + } + else { + while (isdigit(peek(s))) next(s); + is_float = chr(s, '.'); + while (isdigit(peek(s))) next(s); + } + if (s->s + s->pos == start) + return false; + + errno = 0; + if (is_hex) + *out = strtoul(start, &end, 16); + else if (is_float) + *out = strtod(start, &end); + else + *out = strtoul(start, &end, 10); + if (errno != 0 || s->s + s->pos != end) + *out_tok = ERROR_TOK; + else + *out_tok = (is_float ? FLOAT : INTEGER); + return true; +} + +int +_xkbcommon_lex(YYSTYPE *yylval, YYLTYPE *yylloc, struct scanner *s) +{ + enum yytokentype tok; + +skip_more_whitespace_and_comments: + /* Skip spaces. */ + while (isspace(peek(s))) next(s); + + /* Skip comments. */ + if (lit(s, "//") || chr(s, '#')) { + while (!eof(s) && !eol(s)) next(s); + goto skip_more_whitespace_and_comments; + } + + /* See if we're done. */ + if (eof(s)) return END_OF_FILE; + + /* New token. */ + yylloc->first_line = yylloc->last_line = s->line; + yylloc->first_column = yylloc->last_column = s->column; + s->buf_pos = 0; + + /* String literal. */ + if (chr(s, '\"')) { + while (!eof(s) && !eol(s) && peek(s) != '\"') { + if (chr(s, '\\')) { + uint8_t o; + if (chr(s, '\\')) buf_append(s, '\\'); + else if (chr(s, 'n')) buf_append(s, '\n'); + else if (chr(s, 't')) buf_append(s, '\t'); + else if (chr(s, 'r')) buf_append(s, '\r'); + else if (chr(s, 'b')) buf_append(s, '\b'); + else if (chr(s, 'f')) buf_append(s, '\f'); + else if (chr(s, 'v')) buf_append(s, '\v'); + else if (chr(s, 'e')) buf_append(s, '\033'); + else if (oct(s, &o)) buf_append(s, (char) o); + else return scanner_error(yylloc, s, + "illegal escape sequence in string literal"); + } else { + buf_append(s, next(s)); + } + } + if (!buf_append(s, '\0') || !chr(s, '\"')) + return scanner_error(yylloc, s, "unterminated string literal"); + yylval->str = strdup(s->buf); + if (!yylval->str) + return scanner_error(yylloc, s, "scanner out of memory"); + return STRING; + } + + /* Key name literal. */ + if (chr(s, '<')) { + while (isgraph(peek(s)) && peek(s) != '>') + buf_append(s, next(s)); + if (s->buf_pos == 0) + return scanner_error(yylloc, s, "empty key name literal"); + if (!buf_append(s, '\0') || !chr(s, '>')) + return scanner_error(yylloc, s, "unterminated key name literal"); + yylval->sval = xkb_atom_intern(s->ctx, s->buf); + return KEYNAME; + } + + /* Operators and punctuation. */ + if (chr(s, ';')) return SEMI; + if (chr(s, '{')) return OBRACE; + if (chr(s, '}')) return CBRACE; + if (chr(s, '=')) return EQUALS; + if (chr(s, '[')) return OBRACKET; + if (chr(s, ']')) return CBRACKET; + if (chr(s, '(')) return OPAREN; + if (chr(s, ')')) return CPAREN; + if (chr(s, '.')) return DOT; + if (chr(s, ',')) return COMMA; + if (chr(s, '+')) return PLUS; + if (chr(s, '-')) return MINUS; + if (chr(s, '*')) return TIMES; + if (chr(s, '!')) return EXCLAM; + if (chr(s, '~')) return INVERT; + + /* Identifier. */ + if (isalpha(peek(s)) || peek(s) == '_') { + s->buf_pos = 0; + while (isalnum(peek(s)) || peek(s) == '_') + buf_append(s, next(s)); + if (!buf_append(s, '\0')) + return scanner_error(yylloc, s, "identifier too long"); + + /* Keyword. */ + tok = keyword_to_token(s->buf); + if (tok != -1) return tok; + + yylval->str = strdup(s->buf); + if (!yylval->str) + return scanner_error(yylloc, s, "scanner out of memory"); + return IDENT; + } + + /* Number literal (hexadecimal / decimal / float). */ + if (number(s, &yylval->num, &tok)) { + if (tok == ERROR_TOK) + return scanner_error(yylloc, s, "malformed number literal"); + return tok; + } + + return scanner_error(yylloc, s, "unrecognized token"); +} + +XkbFile * +XkbParseString(struct xkb_context *ctx, const char *string, size_t len, + const char *file_name, const char *map) +{ + struct scanner scanner; + scanner_init(&scanner, ctx, string, len, file_name); + return parse(ctx, &scanner, map); +} + +XkbFile * +XkbParseFile(struct xkb_context *ctx, FILE *file, + const char *file_name, const char *map) +{ + bool ok; + XkbFile *xkb_file; + const char *string; + size_t size; + + ok = map_file(file, &string, &size); + if (!ok) { + log_err(ctx, "Couldn't read XKB file %s: %s\n", + file_name, strerror(errno)); + return NULL; + } + + xkb_file = XkbParseString(ctx, string, size, file_name, map); + unmap_file(string, size); + return xkb_file; +} diff --git a/src/xkbcomp/scanner.l b/src/xkbcomp/scanner.l deleted file mode 100644 index f30462d..0000000 --- a/src/xkbcomp/scanner.l +++ /dev/null @@ -1,323 +0,0 @@ -/************************************************************ - 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 "xkbcomp-priv.h" -#include "parser-priv.h" - -#pragma GCC diagnostic ignored "-Wmissing-noreturn" -#pragma GCC diagnostic ignored "-Wredundant-decls" -#pragma GCC diagnostic push - -struct scanner_extra { - struct xkb_context *ctx; - const char *file_name; - char scanBuf[1024]; - char *s; -}; - -static void -scanner_error_extra(struct YYLTYPE *loc, struct scanner_extra *extra, - const char *msg); - -#define YY_USER_ACTION { \ - yylloc->first_line = yylineno; \ - yylloc->last_line = yylineno; \ -} - -#define APPEND_S(ch) do { \ - if (yyextra->s - yyextra->scanBuf >= sizeof(yyextra->scanBuf) - 1) \ - return ERROR_TOK; \ - *yyextra->s++ = ch; \ -} while (0) -%} - -%option prefix="_xkbcommon_" -%option reentrant -%option extra-type="struct scanner_extra *" -%option bison-bridge bison-locations -%option yylineno -%option nounistd noyywrap noinput nounput -%option never-interactive -%option case-insensitive - -%x S_STR - -%% - -"//"[^\n]* -"#"[^\n]* - -\" yyextra->s = yyextra->scanBuf; BEGIN(S_STR); - -\" { - BEGIN(INITIAL); - *yyextra->s = '\0'; - yylval->str = strdup(yyextra->scanBuf); - return STRING; - } - -\\[0-7]{1,3} { - /* octal escape sequence */ - unsigned int result; - - (void) sscanf( yytext + 1, "%o", &result ); - - if (result > 0xff) { - scanner_error_extra(yylloc, yyextra, - "Illegal octal escape"); - return ERROR_TOK; - } - - APPEND_S(result); - } - -\\[0-9]+ { - scanner_error_extra(yylloc, yyextra, - "Illegal octal escape"); - return ERROR_TOK; - } - -\\n APPEND_S('\n'); -\\t APPEND_S('\t'); -\\r APPEND_S('\r'); -\\b APPEND_S('\b'); -\\f APPEND_S('\f'); -\\v APPEND_S('\v'); -\\e APPEND_S('\033'); - -. APPEND_S(yytext[0]); - -\<[a-zA-Z0-9_+-]+\> { - /* We don't want the brackets. */ - yytext[yyleng - 1] = '\0'; - yytext++; - yylval->sval = xkb_atom_intern(yyextra->ctx, yytext); - return KEYNAME; - } - -xkb_keymap return XKB_KEYMAP; -xkb_keycodes return XKB_KEYCODES; -xkb_types return XKB_TYPES; -xkb_symbols return XKB_SYMBOLS; -xkb_compat return XKB_COMPATMAP; -xkb_compat_map return XKB_COMPATMAP; -xkb_compatibility return XKB_COMPATMAP; -xkb_compatibility_map return XKB_COMPATMAP; -xkb_geometry return XKB_GEOMETRY; -xkb_semantics return XKB_SEMANTICS; -xkb_layout return XKB_LAYOUT; -include return INCLUDE; -override return OVERRIDE; -augment return AUGMENT; -replace return REPLACE; -alternate return ALTERNATE; -partial return PARTIAL; -default return DEFAULT; -hidden return HIDDEN; -virtual_modifiers return VIRTUAL_MODS; -type return TYPE; -interpret return INTERPRET; -action return ACTION_TOK; -key return KEY; -alias return ALIAS; -group return GROUP; -modmap return MODIFIER_MAP; -mod_map return MODIFIER_MAP; -modifier_map return MODIFIER_MAP; -indicator return INDICATOR; -shape return SHAPE; -row return ROW; -keys return KEYS; -section return SECTION; -overlay return OVERLAY; -text return TEXT; -outline return OUTLINE; -solid return SOLID; -logo return LOGO; -virtual return VIRTUAL; -alphanumeric_keys return ALPHANUMERIC_KEYS; -modifier_keys return MODIFIER_KEYS; -keypad_keys return KEYPAD_KEYS; -function_keys return FUNCTION_KEYS; -alternate_group return ALTERNATE_GROUP; - -[a-zA-Z_][a-zA-Z_0-9]* yylval->str = strdup(yytext); return IDENT; - -0x[a-fA-F0-9]+ | -[0-9]+ { - char *end; - yylval->num = strtoul(yytext, &end, 0); - - return INTEGER; - } -[0-9]+\.[0-9]+ { - char *end; - yylval->num = strtod(yytext, &end); - - return FLOAT; - } - -"=" return EQUALS; -"+" return PLUS; -"-" return MINUS; -"/" return DIVIDE; -"*" return TIMES; -"{" return OBRACE; -"}" return CBRACE; -"(" return OPAREN; -")" return CPAREN; -"[" return OBRACKET; -"]" return CBRACKET; -"." return DOT; -"," return COMMA; -";" return SEMI; -"!" return EXCLAM; -"~" return INVERT; - -[ \t\r\n\v]+ - -<> return END_OF_FILE; - -. return ERROR_TOK; - -%% - -#pragma GCC diagnostic pop - -static void -scanner_error_extra(struct YYLTYPE *loc, struct scanner_extra *extra, - const char *msg) -{ - log_err(extra->ctx, "%s: line %d of %s\n", msg, - loc->first_line, - extra->file_name ? extra->file_name : "(unknown)"); -} - -void -scanner_error(struct YYLTYPE *loc, void *scanner, const char *msg) -{ - struct scanner_extra *extra = yyget_extra(scanner); - scanner_error_extra(loc, extra, msg); -} - -static bool -init_scanner(yyscan_t *scanner, struct scanner_extra *extra, - struct xkb_context *ctx, const char *file_name) -{ - memset(extra, 0, sizeof(*extra)); - - if (yylex_init_extra(extra, scanner) != 0) - return false; - - extra->ctx = ctx; - extra->file_name = file_name; - - return true; -} - -static void -clear_scanner(yyscan_t scanner) -{ - yylex_destroy(scanner); -} - -XkbFile * -XkbParseString(struct xkb_context *ctx, const char *string, - const char *file_name) -{ - yyscan_t scanner; - struct scanner_extra extra; - YY_BUFFER_STATE state; - XkbFile *xkb_file; - - if (!init_scanner(&scanner, &extra, ctx, file_name)) - return NULL; - - state = yy_scan_string(string, scanner); - - xkb_file = parse(ctx, scanner, NULL); - - yy_delete_buffer(state, scanner); - clear_scanner(scanner); - - return xkb_file; -} - -/* - * yy_scan_buffer() requires the last two bytes of \buf to be 0. These two bytes - * are not scanned. Other zero bytes in the buffer are scanned normally, though. - * Due to these terminating zeroes, \length must be greater than 2. - * Furthermore, the buffer must be writable and you cannot make any assumptions - * about it after the scanner finished. - * All this must be guaranteed by the caller of this function! - */ -XkbFile * -XkbParseBuffer(struct xkb_context *ctx, char *buf, size_t length, - const char *file_name) -{ - yyscan_t scanner; - struct scanner_extra extra; - YY_BUFFER_STATE state; - XkbFile *xkb_file; - - if (!init_scanner(&scanner, &extra, ctx, file_name)) - return NULL; - - xkb_file = NULL; - state = yy_scan_buffer(buf, length, scanner); - if (state) { - xkb_file = parse(ctx, scanner, NULL); - yy_delete_buffer(state, scanner); - } - - clear_scanner(scanner); - - return xkb_file; -} - -XkbFile * -XkbParseFile(struct xkb_context *ctx, FILE *file, - const char *file_name, const char *map) -{ - yyscan_t scanner; - struct scanner_extra extra; - YY_BUFFER_STATE state; - XkbFile *xkb_file; - - if (!init_scanner(&scanner, &extra, ctx, file_name)) - return NULL; - - state = yy_create_buffer(file, YY_BUF_SIZE, scanner); - yy_switch_to_buffer(state, scanner); - - xkb_file = parse(ctx, scanner, map); - - yy_delete_buffer(state, scanner); - clear_scanner(scanner); - - return xkb_file; -} diff --git a/src/xkbcomp/xkbcomp-priv.h b/src/xkbcomp/xkbcomp-priv.h index 4d421b5..97cc2a8 100644 --- a/src/xkbcomp/xkbcomp-priv.h +++ b/src/xkbcomp/xkbcomp-priv.h @@ -45,12 +45,9 @@ XkbParseFile(struct xkb_context *ctx, FILE *file, const char *file_name, const char *map); XkbFile * -XkbParseString(struct xkb_context *ctx, const char *string, - const char *file_name); - -XkbFile * -XkbParseBuffer(struct xkb_context *ctx, char *buf, size_t length, - const char *file_name); +XkbParseString(struct xkb_context *ctx, + const char *string, size_t len, + const char *file_name, const char *map); void FreeXkbFile(XkbFile *file); diff --git a/src/xkbcomp/xkbcomp.c b/src/xkbcomp/xkbcomp.c index b9a1b5f..007e3f7 100644 --- a/src/xkbcomp/xkbcomp.c +++ b/src/xkbcomp/xkbcomp.c @@ -97,12 +97,13 @@ text_v1_keymap_new_from_names(struct xkb_keymap *keymap, } static bool -text_v1_keymap_new_from_string(struct xkb_keymap *keymap, const char *string) +text_v1_keymap_new_from_string(struct xkb_keymap *keymap, + const char *string, size_t len) { bool ok; XkbFile *xkb_file; - xkb_file = XkbParseString(keymap->ctx, string, "(input string)"); + xkb_file = XkbParseString(keymap->ctx, string, len, "(input string)", NULL); if (!xkb_file) { log_err(keymap->ctx, "Failed to parse input xkb string\n"); return NULL; @@ -113,38 +114,6 @@ text_v1_keymap_new_from_string(struct xkb_keymap *keymap, const char *string) return ok; } -static bool -text_v1_keymap_new_from_buffer(struct xkb_keymap *keymap, - const char *buffer, size_t length) -{ - bool ok; - XkbFile *xkb_file; - char *buf; - - buf = malloc(length + 2); - if (!buf) { - log_err(keymap->ctx, "Cannot allocate memory for keymap\n"); - return NULL; - } - - /* yy_scan_buffer requires two terminating zero bytes */ - memcpy(buf, buffer, length); - buf[length] = 0; - buf[length + 1] = 0; - - xkb_file = XkbParseBuffer(keymap->ctx, buf, length + 2, "input"); - if (!xkb_file) { - log_err(keymap->ctx, "Failed to parse input xkb file\n"); - free(buf); - return NULL; - } - - ok = compile_keymap_file(keymap, xkb_file); - FreeXkbFile(xkb_file); - free(buf); - return ok; -} - static bool text_v1_keymap_new_from_file(struct xkb_keymap *keymap, FILE *file) { @@ -165,7 +134,6 @@ text_v1_keymap_new_from_file(struct xkb_keymap *keymap, FILE *file) const struct xkb_keymap_format_ops text_v1_keymap_format_ops = { .keymap_new_from_names = text_v1_keymap_new_from_names, .keymap_new_from_string = text_v1_keymap_new_from_string, - .keymap_new_from_buffer = text_v1_keymap_new_from_buffer, .keymap_new_from_file = text_v1_keymap_new_from_file, .keymap_get_as_string = text_v1_keymap_get_as_string, }; -- cgit v1.2.3