Restructure parser token error printing
The check_closing_token function is changed to the is_closing_tokentype
function. The get_errmsg_unexpected_token function is changed to the
get_errmsg_unexpected_tokentype function. The
print_errmsg_token_unexpected function is added.
The tryparse_redirect function is modified to keep the redundant error
message suppressed.
@@ -284,6 +284,8 @@ | ||
284 | 284 | __attribute__((pure)); |
285 | 285 | static bool is_command_delimiter_tokentype(tokentype_T tt) |
286 | 286 | __attribute__((const)); |
287 | +static bool is_closing_tokentype(tokentype_T tt) | |
288 | + __attribute__((const)); | |
287 | 289 | |
288 | 290 | |
289 | 291 | /* Checks if the specified character can be used in a portable variable name. |
@@ -486,7 +488,28 @@ | ||
486 | 488 | } |
487 | 489 | } |
488 | 490 | |
491 | +/* Determines if the specified token is a 'closing' token such as ")", "}", and | |
492 | + * "fi". Closing tokens delimit and/or lists. */ | |
493 | +bool is_closing_tokentype(tokentype_T tt) | |
494 | +{ | |
495 | + switch (tt) { | |
496 | + case TT_RPAREN: | |
497 | + case TT_RBRACE: | |
498 | + case TT_THEN: | |
499 | + case TT_ELIF: | |
500 | + case TT_ELSE: | |
501 | + case TT_FI: | |
502 | + case TT_DO: | |
503 | + case TT_DONE: | |
504 | + case TT_DOUBLE_SEMICOLON: | |
505 | + case TT_ESAC: | |
506 | + return true; | |
507 | + default: | |
508 | + return false; | |
509 | + } | |
510 | +} | |
489 | 511 | |
512 | + | |
490 | 513 | /********** Parser **********/ |
491 | 514 | |
492 | 515 | /* Holds data that are used in parsing. */ |
@@ -552,8 +575,6 @@ | ||
552 | 575 | __attribute__((pure,nonnull)); |
553 | 576 | static const wchar_t *check_opening_token(parsestate_T *ps) |
554 | 577 | __attribute__((nonnull)); |
555 | -static const wchar_t *check_closing_token(parsestate_T *ps) | |
556 | - __attribute__((nonnull)); | |
557 | 578 | static bool psubstitute_alias(parsestate_T *ps, substaliasflags_T f) |
558 | 579 | __attribute__((nonnull)); |
559 | 580 | static void psubstitute_alias_recursive(parsestate_T *ps, substaliasflags_T f) |
@@ -645,7 +666,9 @@ | ||
645 | 666 | parsestate_T *ps, bool backquote, bool stoponnewline, |
646 | 667 | wordunit_T **lastp) |
647 | 668 | __attribute__((nonnull)); |
648 | -static const char *get_errmsg_unexpected_token(const wchar_t *t) | |
669 | +static const char *get_errmsg_unexpected_tokentype(tokentype_T tokentype) | |
670 | + __attribute__((const)); | |
671 | +static void print_errmsg_token_unexpected(parsestate_T *ps) | |
649 | 672 | __attribute__((nonnull)); |
650 | 673 | static void print_errmsg_token_missing(parsestate_T *ps, const wchar_t *t) |
651 | 674 | __attribute__((nonnull)); |
@@ -1158,30 +1181,6 @@ | ||
1158 | 1181 | return NULL; |
1159 | 1182 | } |
1160 | 1183 | |
1161 | -/* Checks if there is a 'closing' token such as ")", "}", and "fi" at the | |
1162 | - * current position. If there is one, the token string is returned. | |
1163 | - * Otherwise, NULL is returned. | |
1164 | - * This function calls `ensure_buffer(ps, 5)'. */ | |
1165 | -/* Closing tokens delimit and/or lists. */ | |
1166 | -const wchar_t *check_closing_token(parsestate_T *ps) | |
1167 | -{ | |
1168 | - ensure_buffer(ps, 5); | |
1169 | - if (ps->src.contents[ps->index] == L')') | |
1170 | - return L")"; | |
1171 | - if (ps->src.contents[ps->index] == L';' && | |
1172 | - ps->src.contents[ps->index + 1] == L';') | |
1173 | - return L";;"; | |
1174 | - if (has_token(ps, L"}")) return L"}"; | |
1175 | - if (has_token(ps, L"then")) return L"then"; | |
1176 | - if (has_token(ps, L"else")) return L"else"; | |
1177 | - if (has_token(ps, L"elif")) return L"elif"; | |
1178 | - if (has_token(ps, L"fi")) return L"fi"; | |
1179 | - if (has_token(ps, L"do")) return L"do"; | |
1180 | - if (has_token(ps, L"done")) return L"done"; | |
1181 | - if (has_token(ps, L"esac")) return L"esac"; | |
1182 | - return NULL; | |
1183 | -} | |
1184 | - | |
1185 | 1184 | /* Performs alias substitution with the given parse state. Proceeds to the |
1186 | 1185 | * next token if substitution occurred. */ |
1187 | 1186 | bool psubstitute_alias(parsestate_T *ps, substaliasflags_T flags) |
@@ -1242,7 +1241,7 @@ | ||
1242 | 1241 | if (ps->tokentype == TT_END_OF_INPUT) { |
1243 | 1242 | break; |
1244 | 1243 | } else if (ps->tokentype == TT_RPAREN) { |
1245 | - serror(ps, get_errmsg_unexpected_token(L")"), L")"); | |
1244 | + print_errmsg_token_unexpected(ps); | |
1246 | 1245 | break; |
1247 | 1246 | } else if (need_separator) { |
1248 | 1247 | serror(ps, Ngt("`;' or `&' is missing")); |
@@ -1252,7 +1251,7 @@ | ||
1252 | 1251 | if (parse_newline_list(ps)) |
1253 | 1252 | need_separator = false; |
1254 | 1253 | if (need_separator || ps->tokentype == TT_END_OF_INPUT || |
1255 | - check_closing_token(ps)) | |
1254 | + is_closing_tokentype(ps->tokentype)) | |
1256 | 1255 | break; |
1257 | 1256 | } |
1258 | 1257 |
@@ -1416,26 +1415,10 @@ | ||
1416 | 1415 | { |
1417 | 1416 | ps->reparse = false; |
1418 | 1417 | |
1419 | - /* Note: `check_closing_token' calls `ensure_buffer(ps, 5)'. */ | |
1420 | - const wchar_t *t = check_closing_token(ps); | |
1421 | - if (t != NULL) { | |
1422 | - serror(ps, get_errmsg_unexpected_token(t), t); | |
1418 | + if (ps->tokentype == TT_BANG || ps->tokentype == TT_IN || | |
1419 | + is_closing_tokentype(ps->tokentype)) { | |
1420 | + print_errmsg_token_unexpected(ps); | |
1423 | 1421 | return NULL; |
1424 | - } else if (ps->tokentype == TT_BANG) { | |
1425 | - serror(ps, get_errmsg_unexpected_token(L"!"), L"!"); | |
1426 | - return NULL; | |
1427 | - } else if (ps->tokentype == TT_IN) { | |
1428 | - serror(ps, get_errmsg_unexpected_token(L"in"), L"in"); | |
1429 | - return NULL; | |
1430 | - } else if (ps->tokentype == TT_LPAREN) { | |
1431 | - return parse_compound_command(ps); | |
1432 | - } else if (is_command_delimiter_tokentype(ps->tokentype)) { | |
1433 | - if (ps->tokentype == TT_END_OF_INPUT || ps->tokentype == TT_NEWLINE) | |
1434 | - serror(ps, Ngt("a command is missing at the end of input")); | |
1435 | - else | |
1436 | - serror(ps, Ngt("a command is missing before `%lc'"), | |
1437 | - (wint_t) ps->src.contents[ps->index]); | |
1438 | - return NULL; | |
1439 | 1422 | } |
1440 | 1423 | |
1441 | 1424 | command_T *result = parse_compound_command(ps); |
@@ -1456,6 +1439,18 @@ | ||
1456 | 1439 | redir_T **redirlastp = parse_assignments_and_redirects(ps, result); |
1457 | 1440 | result->c_words = parse_words_and_redirects(ps, redirlastp, true); |
1458 | 1441 | |
1442 | + if (result->c_words[0] == NULL && result->c_assigns == NULL && | |
1443 | + result->c_redirs == NULL) { | |
1444 | + /* an empty command */ | |
1445 | + comsfree(result); | |
1446 | + if (ps->tokentype == TT_END_OF_INPUT || ps->tokentype == TT_NEWLINE) | |
1447 | + serror(ps, Ngt("a command is missing at the end of input")); | |
1448 | + else | |
1449 | + serror(ps, Ngt("a command is missing before `%lc'"), | |
1450 | + (wint_t) ps->src.contents[ps->index]); | |
1451 | + return NULL; | |
1452 | + } | |
1453 | + | |
1459 | 1454 | return try_reparse_as_function(ps, result); |
1460 | 1455 | } |
1461 | 1456 |
@@ -1707,16 +1702,15 @@ | ||
1707 | 1702 | parse_here_document_tag: |
1708 | 1703 | next_token(ps); |
1709 | 1704 | psubstitute_alias_recursive(ps, 0); |
1705 | + result->rd_hereend = | |
1706 | + xwcsndup(&ps->src.contents[ps->index], ps->next_index - ps->index); | |
1707 | + result->rd_herecontent = NULL; | |
1710 | 1708 | if (ps->token == NULL) { |
1711 | 1709 | serror(ps, Ngt("the end-of-here-document indicator is missing")); |
1712 | - free(result); | |
1713 | - return NULL; | |
1710 | + } else { | |
1711 | + pl_add(&ps->pending_heredocs, result); | |
1712 | + next_token(ps); | |
1714 | 1713 | } |
1715 | - result->rd_hereend = | |
1716 | - xwcsndup(&ps->src.contents[ps->index], ps->next_index - ps->index); | |
1717 | - result->rd_herecontent = NULL; | |
1718 | - pl_add(&ps->pending_heredocs, result); | |
1719 | - next_token(ps); | |
1720 | 1714 | return result; |
1721 | 1715 | |
1722 | 1716 | parse_command: |
@@ -3031,60 +3025,56 @@ | ||
3031 | 3025 | |
3032 | 3026 | /***** Auxiliaries about Error Messages *****/ |
3033 | 3027 | |
3034 | -const char *get_errmsg_unexpected_token(const wchar_t *t) | |
3028 | +const char *get_errmsg_unexpected_tokentype(tokentype_T tokentype) | |
3035 | 3029 | { |
3036 | - switch (t[0]) { | |
3037 | - case L')': | |
3038 | - assert(wcscmp(t, L")") == 0); | |
3030 | + switch (tokentype) { | |
3031 | + case TT_RPAREN: | |
3039 | 3032 | return Ngt("encountered `%ls' without a matching `('"); |
3040 | - case L'}': | |
3041 | - assert(wcscmp(t, L"}") == 0); | |
3033 | + case TT_RBRACE: | |
3042 | 3034 | return Ngt("encountered `%ls' without a matching `{'"); |
3043 | - case L';': | |
3044 | - assert(wcscmp(t, L";;") == 0); | |
3035 | + case TT_DOUBLE_SEMICOLON: | |
3045 | 3036 | return Ngt("`%ls' is used outside `case'"); |
3046 | - case L'!': | |
3047 | - assert(wcscmp(t, L"!") == 0); | |
3037 | + case TT_BANG: | |
3048 | 3038 | return Ngt("`%ls' cannot be used as a command name"); |
3049 | - case L'i': | |
3050 | - assert(wcscmp(t, L"in") == 0); | |
3039 | + case TT_IN: | |
3051 | 3040 | return Ngt("`%ls' cannot be used as a command name"); |
3052 | - case L'f': | |
3053 | - assert(wcscmp(t, L"fi") == 0); | |
3041 | + case TT_FI: | |
3054 | 3042 | return Ngt("encountered `%ls' " |
3055 | 3043 | "without a matching `if' and/or `then'"); |
3056 | - case L't': | |
3057 | - assert(wcscmp(t, L"then") == 0); | |
3044 | + case TT_THEN: | |
3058 | 3045 | return Ngt("encountered `%ls' without a matching `if' or `elif'"); |
3059 | - case L'd': | |
3060 | - assert(t[1] == L'o'); | |
3061 | - if (t[2] == L'\0') { | |
3062 | - assert(wcscmp(t, L"do") == 0); | |
3063 | - return Ngt("encountered `%ls' " | |
3064 | - "without a matching `for', `while', or `until'"); | |
3065 | - } else { | |
3066 | - assert(wcscmp(t, L"done") == 0); | |
3067 | - return Ngt("encountered `%ls' without a matching `do'"); | |
3068 | - } | |
3069 | - case L'e': | |
3070 | - if (t[1] == L's') { | |
3071 | - assert(wcscmp(t, L"esac") == 0); | |
3072 | - return Ngt("encountered `%ls' without a matching `case'"); | |
3073 | - } else { | |
3074 | - assert(wcscmp(t, L"else") == 0 || wcscmp(t, L"elif") == 0); | |
3075 | - return Ngt("encountered `%ls' " | |
3046 | + case TT_DO: | |
3047 | + return Ngt("encountered `%ls' " | |
3048 | + "without a matching `for', `while', or `until'"); | |
3049 | + case TT_DONE: | |
3050 | + return Ngt("encountered `%ls' without a matching `do'"); | |
3051 | + case TT_ESAC: | |
3052 | + return Ngt("encountered `%ls' without a matching `case'"); | |
3053 | + case TT_ELIF: | |
3054 | + case TT_ELSE: | |
3055 | + return Ngt("encountered `%ls' " | |
3076 | 3056 | "without a matching `if' and/or `then'"); |
3077 | - } | |
3078 | 3057 | default: |
3079 | 3058 | assert(false); |
3080 | 3059 | } |
3081 | 3060 | } |
3082 | 3061 | |
3062 | +void print_errmsg_token_unexpected(parsestate_T *ps) | |
3063 | +{ | |
3064 | + assert(ps->index <= ps->next_index); | |
3065 | + size_t length = ps->next_index - ps->index; | |
3066 | + wchar_t token[length + 1]; | |
3067 | + wcsncpy(token, &ps->src.contents[ps->index], length); | |
3068 | + token[length] = L'\0'; | |
3069 | + | |
3070 | + const char *message = get_errmsg_unexpected_tokentype(ps->tokentype); | |
3071 | + serror(ps, message, token); | |
3072 | +} | |
3073 | + | |
3083 | 3074 | void print_errmsg_token_missing(parsestate_T *ps, const wchar_t *t) |
3084 | 3075 | { |
3085 | - const wchar_t *atoken = check_closing_token(ps); | |
3086 | - if (atoken != NULL) { | |
3087 | - serror(ps, get_errmsg_unexpected_token(atoken), atoken); | |
3076 | + if (is_closing_tokentype(ps->tokentype)) { | |
3077 | + print_errmsg_token_unexpected(ps); | |
3088 | 3078 | serror(ps, Ngt("(maybe you missed `%ls'?)"), t); |
3089 | 3079 | } else { |
3090 | 3080 | serror(ps, Ngt("`%ls' is missing"), t); |