To: vim_dev@googlegroups.com Subject: Patch 8.2.0722 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0722 Problem: Vim9: not handling constant expression for elseif. Solution: Use postponed constants. Delete the code for evaluating a constant expression. Files: src/vim9compile.c *** ../vim-8.2.0721/src/vim9compile.c 2020-05-09 16:44:18.946423824 +0200 --- src/vim9compile.c 2020-05-09 17:30:53.357192919 +0200 *************** *** 3310,3514 **** } } - /* - * Evaluate an expression that is a constant: - * has(arg) - * - * Also handle: - * ! in front logical NOT - * - * Return FAIL if the expression is not a constant. - */ - static int - evaluate_const_expr7(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv) - { - typval_T argvars[2]; - char_u *start_leader, *end_leader; - int has_call = FALSE; - - /* - * Skip '!' characters. They are handled later. - * TODO: '-' and '+' characters - */ - start_leader = *arg; - while (**arg == '!') - *arg = skipwhite(*arg + 1); - end_leader = *arg; - - /* - * Recognize only a few types of constants for now. - */ - if (STRNCMP("true", *arg, 4) == 0 && !ASCII_ISALNUM((*arg)[4])) - { - tv->v_type = VAR_BOOL; - tv->vval.v_number = VVAL_TRUE; - *arg += 4; - return OK; - } - if (STRNCMP("false", *arg, 5) == 0 && !ASCII_ISALNUM((*arg)[5])) - { - tv->v_type = VAR_BOOL; - tv->vval.v_number = VVAL_FALSE; - *arg += 5; - return OK; - } - - if (STRNCMP("has(", *arg, 4) == 0) - { - has_call = TRUE; - *arg = skipwhite(*arg + 4); - } - - if (**arg == '"') - { - if (get_string_tv(arg, tv, TRUE) == FAIL) - return FAIL; - } - else if (**arg == '\'') - { - if (get_lit_string_tv(arg, tv, TRUE) == FAIL) - return FAIL; - } - else - return FAIL; - - if (has_call) - { - *arg = skipwhite(*arg); - if (**arg != ')') - return FAIL; - *arg = *arg + 1; - - argvars[0] = *tv; - argvars[1].v_type = VAR_UNKNOWN; - tv->v_type = VAR_NUMBER; - tv->vval.v_number = 0; - f_has(argvars, tv); - clear_tv(&argvars[0]); - - while (start_leader < end_leader) - { - if (*start_leader == '!') - tv->vval.v_number = !tv->vval.v_number; - ++start_leader; - } - } - else if (end_leader > start_leader) - { - clear_tv(tv); - return FAIL; - } - - return OK; - } - - /* - * * number multiplication - * / number division - * % number modulo - */ - static int - evaluate_const_expr6(char_u **arg, cctx_T *cctx, typval_T *tv) - { - char_u *op; - - // get the first variable - if (evaluate_const_expr7(arg, cctx, tv) == FAIL) - return FAIL; - - /* - * Repeat computing, until no "*", "/" or "%" is following. - */ - for (;;) - { - op = skipwhite(*arg); - if (*op != '*' && *op != '/' && *op != '%') - break; - // TODO: not implemented yet. - clear_tv(tv); - return FAIL; - } - return OK; - } - - /* - * + number addition - * - number subtraction - * .. string concatenation - */ - static int - evaluate_const_expr5(char_u **arg, cctx_T *cctx, typval_T *tv) - { - char_u *op; - int oplen; - - // get the first variable - if (evaluate_const_expr6(arg, cctx, tv) == FAIL) - return FAIL; - - /* - * Repeat computing, until no "+", "-" or ".." is following. - */ - for (;;) - { - op = skipwhite(*arg); - if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.'))) - break; - oplen = (*op == '.' ? 2 : 1); - - if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[oplen])) - { - clear_tv(tv); - return FAIL; - } - - if (*op == '.' && tv->v_type == VAR_STRING) - { - typval_T tv2; - size_t len1; - char_u *s1, *s2; - - tv2.v_type = VAR_UNKNOWN; - *arg = skipwhite(op + oplen); - - // TODO: what if we fail??? - if (may_get_next_line(op + oplen, arg, cctx) == FAIL) - return FAIL; - - // get the second variable - if (evaluate_const_expr6(arg, cctx, &tv2) == FAIL) - { - clear_tv(tv); - return FAIL; - } - if (tv2.v_type != VAR_STRING) - { - clear_tv(tv); - clear_tv(&tv2); - return FAIL; - } - s1 = tv->vval.v_string; - len1 = STRLEN(s1); - s2 = tv2.vval.v_string; - tv->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1)); - if (tv->vval.v_string == NULL) - { - vim_free(s1); - vim_free(s2); - return FAIL; - } - mch_memmove(tv->vval.v_string, s1, len1); - STRCPY(tv->vval.v_string + len1, s2); - continue; - } - - // TODO: Not implemented yet. - clear_tv(tv); - return FAIL; - } - return OK; - } - static exptype_T get_compare_type(char_u *p, int *len, int *type_is) { --- 3310,3315 ---- *************** *** 3561,3764 **** } /* - * Only comparing strings is supported right now. - * expr5a == expr5b - */ - static int - evaluate_const_expr4(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv) - { - exptype_T type = EXPR_UNKNOWN; - char_u *p; - int len = 2; - int type_is = FALSE; - - // get the first variable - if (evaluate_const_expr5(arg, cctx, tv) == FAIL) - return FAIL; - - p = skipwhite(*arg); - type = get_compare_type(p, &len, &type_is); - - /* - * If there is a comparative operator, use it. - */ - if (type != EXPR_UNKNOWN) - { - typval_T tv2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - int n; - - // TODO: Only string == string is supported now - if (tv->v_type != VAR_STRING) - return FAIL; - if (type != EXPR_EQUAL) - return FAIL; - - // get the second variable - init_tv(&tv2); - *arg = skipwhite(p + len); - if (evaluate_const_expr5(arg, cctx, &tv2) == FAIL - || tv2.v_type != VAR_STRING) - { - clear_tv(&tv2); - return FAIL; - } - s1 = tv_get_string_buf(tv, buf1); - s2 = tv_get_string_buf(&tv2, buf2); - n = STRCMP(s1, s2); - clear_tv(tv); - clear_tv(&tv2); - tv->v_type = VAR_BOOL; - tv->vval.v_number = n == 0 ? VVAL_TRUE : VVAL_FALSE; - } - - return OK; - } - - static int evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv); - - /* - * Compile constant || or &&. - */ - static int - evaluate_const_and_or(char_u **arg, cctx_T *cctx, char *op, typval_T *tv) - { - char_u *p = skipwhite(*arg); - int opchar = *op; - - if (p[0] == opchar && p[1] == opchar) - { - int val = tv2bool(tv); - - /* - * Repeat until there is no following "||" or "&&" - */ - while (p[0] == opchar && p[1] == opchar) - { - typval_T tv2; - - if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[2])) - return FAIL; - - // eval the next expression - *arg = skipwhite(p + 2); - tv2.v_type = VAR_UNKNOWN; - tv2.v_lock = 0; - if ((opchar == '|' ? evaluate_const_expr3(arg, cctx, &tv2) - : evaluate_const_expr4(arg, cctx, &tv2)) == FAIL) - { - clear_tv(&tv2); - return FAIL; - } - if ((opchar == '&') == val) - { - // false || tv2 or true && tv2: use tv2 - clear_tv(tv); - *tv = tv2; - val = tv2bool(tv); - } - else - clear_tv(&tv2); - p = skipwhite(*arg); - } - } - - return OK; - } - - /* - * Evaluate an expression that is a constant: expr4 && expr4 && expr4 - * Return FAIL if the expression is not a constant. - */ - static int - evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv) - { - // evaluate the first expression - if (evaluate_const_expr4(arg, cctx, tv) == FAIL) - return FAIL; - - // || and && work almost the same - return evaluate_const_and_or(arg, cctx, "&&", tv); - } - - /* - * Evaluate an expression that is a constant: expr3 || expr3 || expr3 - * Return FAIL if the expression is not a constant. - */ - static int - evaluate_const_expr2(char_u **arg, cctx_T *cctx, typval_T *tv) - { - // evaluate the first expression - if (evaluate_const_expr3(arg, cctx, tv) == FAIL) - return FAIL; - - // || and && work almost the same - return evaluate_const_and_or(arg, cctx, "||", tv); - } - - /* - * Evaluate an expression that is a constant: expr2 ? expr1 : expr1 - * E.g. for "has('feature')". - * This does not produce error messages. "tv" should be cleared afterwards. - * Return FAIL if the expression is not a constant. - */ - static int - evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv) - { - char_u *p; - - // evaluate the first expression - if (evaluate_const_expr2(arg, cctx, tv) == FAIL) - return FAIL; - - p = skipwhite(*arg); - if (*p == '?') - { - int val = tv2bool(tv); - typval_T tv2; - - // require space before and after the ? - if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) - return FAIL; - - // evaluate the second expression; any type is accepted - clear_tv(tv); - *arg = skipwhite(p + 1); - if (evaluate_const_expr1(arg, cctx, tv) == FAIL) - return FAIL; - - // Check for the ":". - p = skipwhite(*arg); - if (*p != ':' || !VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) - return FAIL; - - // evaluate the third expression - *arg = skipwhite(p + 1); - tv2.v_type = VAR_UNKNOWN; - if (evaluate_const_expr1(arg, cctx, &tv2) == FAIL) - { - clear_tv(&tv2); - return FAIL; - } - if (val) - { - // use the expr after "?" - clear_tv(&tv2); - } - else - { - // use the expr after ":" - clear_tv(tv); - *tv = tv2; - } - } - return OK; - } - - static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst); - - /* * Compile code to apply '-', '+' and '!'. */ static int --- 3362,3367 ---- *************** *** 4471,4476 **** --- 4074,4081 ---- return OK; } + static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst); + /* * Compile || or &&. */ *************** *** 5727,5735 **** { char_u *p = arg; garray_T *instr = &cctx->ctx_instr; isn_T *isn; scope_T *scope = cctx->ctx_scope; ! typval_T tv; if (scope == NULL || scope->se_type != IF_SCOPE) { --- 5332,5341 ---- { char_u *p = arg; garray_T *instr = &cctx->ctx_instr; + int instr_count = instr->ga_len; isn_T *isn; scope_T *scope = cctx->ctx_scope; ! ppconst_T ppconst; if (scope == NULL || scope->se_type != IF_SCOPE) { *************** *** 5749,5772 **** } // compile "expr"; if we know it evaluates to FALSE skip the block ! tv.v_type = VAR_UNKNOWN; ! if (evaluate_const_expr1(&p, cctx, &tv) == OK) ! cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE; else - cctx->ctx_skip = MAYBE; - clear_tv(&tv); - if (cctx->ctx_skip == MAYBE) { ! p = arg; ! if (compile_expr0(&p, cctx) == FAIL) return NULL; // "where" is set when ":elseif", "else" or ":endif" is found scope->se_u.se_if.is_if_label = instr->ga_len; generate_JUMP(cctx, JUMP_IF_FALSE, 0); } - else - scope->se_u.se_if.is_if_label = -1; return p; } --- 5355,5385 ---- } // compile "expr"; if we know it evaluates to FALSE skip the block ! CLEAR_FIELD(ppconst); ! if (compile_expr1(&p, cctx, &ppconst) == FAIL) ! { ! clear_ppconst(&ppconst); ! return NULL; ! } ! if (instr->ga_len == instr_count && ppconst.pp_used == 1) ! { ! // The expression results in a constant. ! // TODO: how about nesting? ! cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE; ! clear_ppconst(&ppconst); ! scope->se_u.se_if.is_if_label = -1; ! } else { ! // Not a constant, generate instructions for the expression. ! cctx->ctx_skip = MAYBE; ! if (generate_ppconst(cctx, &ppconst) == FAIL) return NULL; // "where" is set when ":elseif", "else" or ":endif" is found scope->se_u.se_if.is_if_label = instr->ga_len; generate_JUMP(cctx, JUMP_IF_FALSE, 0); } return p; } *** ../vim-8.2.0721/src/version.c 2020-05-09 16:44:18.946423824 +0200 --- src/version.c 2020-05-09 17:27:59.773776608 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 722, /**/ -- If bankers can count, how come they have eight windows and only four tellers? /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///