diff --git a/qse/cmd/xli/xli.c b/qse/cmd/xli/xli.c index adb7c85b..d0e56780 100644 --- a/qse/cmd/xli/xli.c +++ b/qse/cmd/xli/xli.c @@ -135,6 +135,7 @@ static void print_usage (QSE_FILE* out, int argc, qse_char_t* argv[]) qse_fprintf (out, QSE_T(" -f keep file inclusion info\n")); qse_fprintf (out, QSE_T(" -t keep comment text\n")); qse_fprintf (out, QSE_T(" -s allow multi-segmented strings\n")); + qse_fprintf (out, QSE_T(" -d allow a leading digit in identifiers\n")); qse_fprintf (out, QSE_T(" -v perform validation\n")); qse_fprintf (out, QSE_T(" -m number specify the maximum amount of memory to use in bytes\n")); #if defined(QSE_BUILD_DEBUG) @@ -162,9 +163,9 @@ static int handle_args (int argc, qse_char_t* argv[]) static qse_opt_t opt = { #if defined(QSE_BUILD_DEBUG) - QSE_T("hi:o:uaftsvm:X:"), + QSE_T("hi:o:uaftsdvm:X:"), #else - QSE_T("hi:o:uaftsvm:"), + QSE_T("hi:o:uaftsdvm:"), #endif lng }; @@ -226,6 +227,10 @@ static int handle_args (int argc, qse_char_t* argv[]) g_trait |= QSE_XLI_MULSEGSTR; break; + case QSE_T('d'): + g_trait |= QSE_XLI_LEADDIGIT; + break; + case QSE_T('v'): g_trait |= QSE_XLI_VALIDATE; break; diff --git a/qse/include/qse/xli/xli.h b/qse/include/qse/xli/xli.h index 7ea043ff..7e2842bf 100644 --- a/qse/include/qse/xli/xli.h +++ b/qse/include/qse/xli/xli.h @@ -50,6 +50,7 @@ enum qse_xli_errnum_t QSE_XLI_ELXCHR, /**< invalid character '${0} */ QSE_XLI_EXKWNR, /**< @word '${0}' not recognized */ QSE_XLI_EXKWEM, /**< @ not followed by a valid word */ + QSE_XLI_EIDENT, /**< invalid identifier '${0}' */ QSE_XLI_EUDKEY, /**< undefined key '${0}' */ QSE_XLI_ENOALI, /**< no alias for '${0}' */ QSE_XLI_EILVAL, /**< illegal value for '${0}' */ @@ -86,7 +87,8 @@ enum qse_xli_trait_t QSE_XLI_KEEPFILE = (1 << 4), /**< keep inclusion file info */ QSE_XLI_MULSEGSTR = (1 << 5), /**< support multi-segmented string */ - QSE_XLI_VALIDATE = (1 << 6) + QSE_XLI_LEADDIGIT = (1 << 6), /**< allow a leading digit in an identifier */ + QSE_XLI_VALIDATE = (1 << 7) }; typedef enum qse_xli_trait_t qse_xli_trait_t; diff --git a/qse/lib/xli/err.c b/qse/lib/xli/err.c index af0f064a..5f956abd 100644 --- a/qse/lib/xli/err.c +++ b/qse/lib/xli/err.c @@ -49,6 +49,7 @@ const qse_char_t* qse_xli_dflerrstr ( QSE_T("invalid character '${0}'"), QSE_T("'${0}' not recognized"), QSE_T("@ not followed by a valid word"), + QSE_T("invalid identifier '${0}'"), QSE_T("undefined key '${0}'"), QSE_T("no alias for '${0}'"), QSE_T("illegal value for '${0}'"), diff --git a/qse/lib/xli/read.c b/qse/lib/xli/read.c index 857ee151..4d542bbc 100644 --- a/qse/lib/xli/read.c +++ b/qse/lib/xli/read.c @@ -104,6 +104,7 @@ struct kwent_t int type; }; +/* note that the keyword must start with @. */ static kwent_t kwtab[] = { /* keep it sorted by the first field for binary search */ @@ -420,50 +421,84 @@ retry: } else if (c == QSE_T('@')) { + /* keyword/directive - start with @ */ + int type; ADD_TOKEN_CHAR (xli, tok, c); GET_CHAR_TO (xli, c); - if (c != QSE_T('_') && !QSE_ISALPHA (c)) + if (!QSE_ISALPHA (c)) { - /* this directive is empty, - * not followed by a valid word */ + /* this directive is empty, not followed by a valid word */ qse_xli_seterror (xli, QSE_XLI_EXKWEM, QSE_NULL, &xli->tok.loc); return -1; } - /* expect normal identifier starting with an alphabet */ + /* expect an identifier starting with an alphabet. the identifier + * forming a keyword/directory is composed of alphabets. */ do { ADD_TOKEN_CHAR (xli, tok, c); GET_CHAR_TO (xli, c); } - while (c == QSE_T('_') || c == QSE_T('-') || QSE_ISALNUM (c)); + while (QSE_ISALPHA (c)); type = classify_ident (xli, QSE_STR_CSTR(tok->name)); if (type == TOK_IDENT) { - /* this directive is not recognized */ + /* this keyword/directive is not recognized */ qse_xli_seterror (xli, QSE_XLI_EXKWNR, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc); return -1; } SET_TOKEN_TYPE (xli, tok, type); } - else if (c == QSE_T('_') || QSE_ISALPHA (c)) + else if (c == QSE_T('_') || QSE_ISALPHA (c) || + (!(xli->tok_status & TOK_STATUS_ENABLE_NSTR) && + (xli->opt.trait & QSE_XLI_LEADDIGIT) && + QSE_ISDIGIT(c))) { - int type; + int lead_digit = QSE_ISDIGIT(c); + int all_digits = 1; - /* identifier */ - do + /* a normal identifier can be composed of wider varieties of characters + * than a keyword/directive */ + while (1) { ADD_TOKEN_CHAR (xli, tok, c); GET_CHAR_TO (xli, c); - } - while (c == QSE_T('_') || c == QSE_T('-') || QSE_ISALNUM (c)); - type = classify_ident (xli, QSE_STR_CSTR(tok->name)); - SET_TOKEN_TYPE (xli, tok, type); + if (c == QSE_T('_') || c == QSE_T('-') || + c == QSE_T(':') || c == QSE_T('*') || + c == QSE_T('/') || QSE_ISALPHA (c)) + { + all_digits = 0; + } + else if (lead_digit && QSE_ISDIGIT(c)) + { + /* nothing to do */ + } + else break; + } + + if (lead_digit && all_digits) + { + /* if an identifier begins with a digit, it must contain a non-digits character */ + qse_xli_seterror (xli, QSE_XLI_EIDENT, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc); + return -1; + } + + SET_TOKEN_TYPE (xli, tok, TOK_IDENT); + } + else if ((xli->tok_status & TOK_STATUS_ENABLE_NSTR) && QSE_ISDIGIT(c)) + { + SET_TOKEN_TYPE (xli, tok, TOK_NSTR); + do + { + ADD_TOKEN_CHAR (xli, tok, c); + GET_CHAR_TO (xli, c); + } + while (QSE_ISDIGIT(c)); } else if (c == QSE_T('\'')) { @@ -533,16 +568,6 @@ retry: } } } - else if ((xli->tok_status & TOK_STATUS_ENABLE_NSTR) && QSE_ISDIGIT(c)) - { - SET_TOKEN_TYPE (xli, tok, TOK_NSTR); - do - { - ADD_TOKEN_CHAR (xli, tok, c); - GET_CHAR_TO (xli, c); - } - while (QSE_ISDIGIT(c)); - } else { n = get_symbols (xli, c, tok); @@ -667,6 +692,7 @@ static int read_pair (qse_xli_t* xli) } } + /* once the key name is read, enable the numeric string for a value */ xli->tok_status |= TOK_STATUS_ENABLE_NSTR; if (get_token (xli) <= -1) goto oops; @@ -705,7 +731,7 @@ static int read_pair (qse_xli_t* xli) { /* SCM_KEYALIAS is specified for this particular item. Let the alias be required. * If KEYALIAS is globally specified with the specific one, it's optional. */ - qse_xli_seterrnum (xli, QSE_XLI_ENOALI, &key); + qse_xli_seterrnum (xli, QSE_XLI_ENOALI, (const qse_cstr_t*)&key); goto oops; } } @@ -771,6 +797,8 @@ static int read_pair (qse_xli_t* xli) goto oops; } + /* semicolon read. turn off NSTR */ + xli->tok_status &= ~TOK_STATUS_ENABLE_NSTR; if (get_token (xli) <= -1) goto oops; } else