Mercurial > vim
comparison src/vim9compile.c @ 20334:b774655968b4 v8.2.0722
patch 8.2.0722: Vim9: not handling constant expression for elseif
Commit: https://github.com/vim/vim/commit/7f14155f422ffcc997fc5a0b635a20a4bafd2093
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat May 9 17:35:53 2020 +0200
patch 8.2.0722: Vim9: not handling constant expression for elseif
Problem: Vim9: not handling constant expression for elseif.
Solution: Use postponed constants. Delete the code for evaluating a
constant expression.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 09 May 2020 17:45:03 +0200 |
parents | a407e243c55c |
children | 2fd980fb9ab3 |
comparison
equal
deleted
inserted
replaced
20333:f4c695970c1f | 20334:b774655968b4 |
---|---|
3306 { | 3306 { |
3307 rettv->v_type = VAR_SPECIAL; | 3307 rettv->v_type = VAR_SPECIAL; |
3308 rettv->vval.v_number = VVAL_NONE; | 3308 rettv->vval.v_number = VVAL_NONE; |
3309 *arg += 6; | 3309 *arg += 6; |
3310 } | 3310 } |
3311 } | |
3312 | |
3313 /* | |
3314 * Evaluate an expression that is a constant: | |
3315 * has(arg) | |
3316 * | |
3317 * Also handle: | |
3318 * ! in front logical NOT | |
3319 * | |
3320 * Return FAIL if the expression is not a constant. | |
3321 */ | |
3322 static int | |
3323 evaluate_const_expr7(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv) | |
3324 { | |
3325 typval_T argvars[2]; | |
3326 char_u *start_leader, *end_leader; | |
3327 int has_call = FALSE; | |
3328 | |
3329 /* | |
3330 * Skip '!' characters. They are handled later. | |
3331 * TODO: '-' and '+' characters | |
3332 */ | |
3333 start_leader = *arg; | |
3334 while (**arg == '!') | |
3335 *arg = skipwhite(*arg + 1); | |
3336 end_leader = *arg; | |
3337 | |
3338 /* | |
3339 * Recognize only a few types of constants for now. | |
3340 */ | |
3341 if (STRNCMP("true", *arg, 4) == 0 && !ASCII_ISALNUM((*arg)[4])) | |
3342 { | |
3343 tv->v_type = VAR_BOOL; | |
3344 tv->vval.v_number = VVAL_TRUE; | |
3345 *arg += 4; | |
3346 return OK; | |
3347 } | |
3348 if (STRNCMP("false", *arg, 5) == 0 && !ASCII_ISALNUM((*arg)[5])) | |
3349 { | |
3350 tv->v_type = VAR_BOOL; | |
3351 tv->vval.v_number = VVAL_FALSE; | |
3352 *arg += 5; | |
3353 return OK; | |
3354 } | |
3355 | |
3356 if (STRNCMP("has(", *arg, 4) == 0) | |
3357 { | |
3358 has_call = TRUE; | |
3359 *arg = skipwhite(*arg + 4); | |
3360 } | |
3361 | |
3362 if (**arg == '"') | |
3363 { | |
3364 if (get_string_tv(arg, tv, TRUE) == FAIL) | |
3365 return FAIL; | |
3366 } | |
3367 else if (**arg == '\'') | |
3368 { | |
3369 if (get_lit_string_tv(arg, tv, TRUE) == FAIL) | |
3370 return FAIL; | |
3371 } | |
3372 else | |
3373 return FAIL; | |
3374 | |
3375 if (has_call) | |
3376 { | |
3377 *arg = skipwhite(*arg); | |
3378 if (**arg != ')') | |
3379 return FAIL; | |
3380 *arg = *arg + 1; | |
3381 | |
3382 argvars[0] = *tv; | |
3383 argvars[1].v_type = VAR_UNKNOWN; | |
3384 tv->v_type = VAR_NUMBER; | |
3385 tv->vval.v_number = 0; | |
3386 f_has(argvars, tv); | |
3387 clear_tv(&argvars[0]); | |
3388 | |
3389 while (start_leader < end_leader) | |
3390 { | |
3391 if (*start_leader == '!') | |
3392 tv->vval.v_number = !tv->vval.v_number; | |
3393 ++start_leader; | |
3394 } | |
3395 } | |
3396 else if (end_leader > start_leader) | |
3397 { | |
3398 clear_tv(tv); | |
3399 return FAIL; | |
3400 } | |
3401 | |
3402 return OK; | |
3403 } | |
3404 | |
3405 /* | |
3406 * * number multiplication | |
3407 * / number division | |
3408 * % number modulo | |
3409 */ | |
3410 static int | |
3411 evaluate_const_expr6(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3412 { | |
3413 char_u *op; | |
3414 | |
3415 // get the first variable | |
3416 if (evaluate_const_expr7(arg, cctx, tv) == FAIL) | |
3417 return FAIL; | |
3418 | |
3419 /* | |
3420 * Repeat computing, until no "*", "/" or "%" is following. | |
3421 */ | |
3422 for (;;) | |
3423 { | |
3424 op = skipwhite(*arg); | |
3425 if (*op != '*' && *op != '/' && *op != '%') | |
3426 break; | |
3427 // TODO: not implemented yet. | |
3428 clear_tv(tv); | |
3429 return FAIL; | |
3430 } | |
3431 return OK; | |
3432 } | |
3433 | |
3434 /* | |
3435 * + number addition | |
3436 * - number subtraction | |
3437 * .. string concatenation | |
3438 */ | |
3439 static int | |
3440 evaluate_const_expr5(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3441 { | |
3442 char_u *op; | |
3443 int oplen; | |
3444 | |
3445 // get the first variable | |
3446 if (evaluate_const_expr6(arg, cctx, tv) == FAIL) | |
3447 return FAIL; | |
3448 | |
3449 /* | |
3450 * Repeat computing, until no "+", "-" or ".." is following. | |
3451 */ | |
3452 for (;;) | |
3453 { | |
3454 op = skipwhite(*arg); | |
3455 if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.'))) | |
3456 break; | |
3457 oplen = (*op == '.' ? 2 : 1); | |
3458 | |
3459 if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[oplen])) | |
3460 { | |
3461 clear_tv(tv); | |
3462 return FAIL; | |
3463 } | |
3464 | |
3465 if (*op == '.' && tv->v_type == VAR_STRING) | |
3466 { | |
3467 typval_T tv2; | |
3468 size_t len1; | |
3469 char_u *s1, *s2; | |
3470 | |
3471 tv2.v_type = VAR_UNKNOWN; | |
3472 *arg = skipwhite(op + oplen); | |
3473 | |
3474 // TODO: what if we fail??? | |
3475 if (may_get_next_line(op + oplen, arg, cctx) == FAIL) | |
3476 return FAIL; | |
3477 | |
3478 // get the second variable | |
3479 if (evaluate_const_expr6(arg, cctx, &tv2) == FAIL) | |
3480 { | |
3481 clear_tv(tv); | |
3482 return FAIL; | |
3483 } | |
3484 if (tv2.v_type != VAR_STRING) | |
3485 { | |
3486 clear_tv(tv); | |
3487 clear_tv(&tv2); | |
3488 return FAIL; | |
3489 } | |
3490 s1 = tv->vval.v_string; | |
3491 len1 = STRLEN(s1); | |
3492 s2 = tv2.vval.v_string; | |
3493 tv->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1)); | |
3494 if (tv->vval.v_string == NULL) | |
3495 { | |
3496 vim_free(s1); | |
3497 vim_free(s2); | |
3498 return FAIL; | |
3499 } | |
3500 mch_memmove(tv->vval.v_string, s1, len1); | |
3501 STRCPY(tv->vval.v_string + len1, s2); | |
3502 continue; | |
3503 } | |
3504 | |
3505 // TODO: Not implemented yet. | |
3506 clear_tv(tv); | |
3507 return FAIL; | |
3508 } | |
3509 return OK; | |
3510 } | 3311 } |
3511 | 3312 |
3512 static exptype_T | 3313 static exptype_T |
3513 get_compare_type(char_u *p, int *len, int *type_is) | 3314 get_compare_type(char_u *p, int *len, int *type_is) |
3514 { | 3315 { |
3559 } | 3360 } |
3560 return type; | 3361 return type; |
3561 } | 3362 } |
3562 | 3363 |
3563 /* | 3364 /* |
3564 * Only comparing strings is supported right now. | |
3565 * expr5a == expr5b | |
3566 */ | |
3567 static int | |
3568 evaluate_const_expr4(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv) | |
3569 { | |
3570 exptype_T type = EXPR_UNKNOWN; | |
3571 char_u *p; | |
3572 int len = 2; | |
3573 int type_is = FALSE; | |
3574 | |
3575 // get the first variable | |
3576 if (evaluate_const_expr5(arg, cctx, tv) == FAIL) | |
3577 return FAIL; | |
3578 | |
3579 p = skipwhite(*arg); | |
3580 type = get_compare_type(p, &len, &type_is); | |
3581 | |
3582 /* | |
3583 * If there is a comparative operator, use it. | |
3584 */ | |
3585 if (type != EXPR_UNKNOWN) | |
3586 { | |
3587 typval_T tv2; | |
3588 char_u *s1, *s2; | |
3589 char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; | |
3590 int n; | |
3591 | |
3592 // TODO: Only string == string is supported now | |
3593 if (tv->v_type != VAR_STRING) | |
3594 return FAIL; | |
3595 if (type != EXPR_EQUAL) | |
3596 return FAIL; | |
3597 | |
3598 // get the second variable | |
3599 init_tv(&tv2); | |
3600 *arg = skipwhite(p + len); | |
3601 if (evaluate_const_expr5(arg, cctx, &tv2) == FAIL | |
3602 || tv2.v_type != VAR_STRING) | |
3603 { | |
3604 clear_tv(&tv2); | |
3605 return FAIL; | |
3606 } | |
3607 s1 = tv_get_string_buf(tv, buf1); | |
3608 s2 = tv_get_string_buf(&tv2, buf2); | |
3609 n = STRCMP(s1, s2); | |
3610 clear_tv(tv); | |
3611 clear_tv(&tv2); | |
3612 tv->v_type = VAR_BOOL; | |
3613 tv->vval.v_number = n == 0 ? VVAL_TRUE : VVAL_FALSE; | |
3614 } | |
3615 | |
3616 return OK; | |
3617 } | |
3618 | |
3619 static int evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv); | |
3620 | |
3621 /* | |
3622 * Compile constant || or &&. | |
3623 */ | |
3624 static int | |
3625 evaluate_const_and_or(char_u **arg, cctx_T *cctx, char *op, typval_T *tv) | |
3626 { | |
3627 char_u *p = skipwhite(*arg); | |
3628 int opchar = *op; | |
3629 | |
3630 if (p[0] == opchar && p[1] == opchar) | |
3631 { | |
3632 int val = tv2bool(tv); | |
3633 | |
3634 /* | |
3635 * Repeat until there is no following "||" or "&&" | |
3636 */ | |
3637 while (p[0] == opchar && p[1] == opchar) | |
3638 { | |
3639 typval_T tv2; | |
3640 | |
3641 if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[2])) | |
3642 return FAIL; | |
3643 | |
3644 // eval the next expression | |
3645 *arg = skipwhite(p + 2); | |
3646 tv2.v_type = VAR_UNKNOWN; | |
3647 tv2.v_lock = 0; | |
3648 if ((opchar == '|' ? evaluate_const_expr3(arg, cctx, &tv2) | |
3649 : evaluate_const_expr4(arg, cctx, &tv2)) == FAIL) | |
3650 { | |
3651 clear_tv(&tv2); | |
3652 return FAIL; | |
3653 } | |
3654 if ((opchar == '&') == val) | |
3655 { | |
3656 // false || tv2 or true && tv2: use tv2 | |
3657 clear_tv(tv); | |
3658 *tv = tv2; | |
3659 val = tv2bool(tv); | |
3660 } | |
3661 else | |
3662 clear_tv(&tv2); | |
3663 p = skipwhite(*arg); | |
3664 } | |
3665 } | |
3666 | |
3667 return OK; | |
3668 } | |
3669 | |
3670 /* | |
3671 * Evaluate an expression that is a constant: expr4 && expr4 && expr4 | |
3672 * Return FAIL if the expression is not a constant. | |
3673 */ | |
3674 static int | |
3675 evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3676 { | |
3677 // evaluate the first expression | |
3678 if (evaluate_const_expr4(arg, cctx, tv) == FAIL) | |
3679 return FAIL; | |
3680 | |
3681 // || and && work almost the same | |
3682 return evaluate_const_and_or(arg, cctx, "&&", tv); | |
3683 } | |
3684 | |
3685 /* | |
3686 * Evaluate an expression that is a constant: expr3 || expr3 || expr3 | |
3687 * Return FAIL if the expression is not a constant. | |
3688 */ | |
3689 static int | |
3690 evaluate_const_expr2(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3691 { | |
3692 // evaluate the first expression | |
3693 if (evaluate_const_expr3(arg, cctx, tv) == FAIL) | |
3694 return FAIL; | |
3695 | |
3696 // || and && work almost the same | |
3697 return evaluate_const_and_or(arg, cctx, "||", tv); | |
3698 } | |
3699 | |
3700 /* | |
3701 * Evaluate an expression that is a constant: expr2 ? expr1 : expr1 | |
3702 * E.g. for "has('feature')". | |
3703 * This does not produce error messages. "tv" should be cleared afterwards. | |
3704 * Return FAIL if the expression is not a constant. | |
3705 */ | |
3706 static int | |
3707 evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3708 { | |
3709 char_u *p; | |
3710 | |
3711 // evaluate the first expression | |
3712 if (evaluate_const_expr2(arg, cctx, tv) == FAIL) | |
3713 return FAIL; | |
3714 | |
3715 p = skipwhite(*arg); | |
3716 if (*p == '?') | |
3717 { | |
3718 int val = tv2bool(tv); | |
3719 typval_T tv2; | |
3720 | |
3721 // require space before and after the ? | |
3722 if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) | |
3723 return FAIL; | |
3724 | |
3725 // evaluate the second expression; any type is accepted | |
3726 clear_tv(tv); | |
3727 *arg = skipwhite(p + 1); | |
3728 if (evaluate_const_expr1(arg, cctx, tv) == FAIL) | |
3729 return FAIL; | |
3730 | |
3731 // Check for the ":". | |
3732 p = skipwhite(*arg); | |
3733 if (*p != ':' || !VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) | |
3734 return FAIL; | |
3735 | |
3736 // evaluate the third expression | |
3737 *arg = skipwhite(p + 1); | |
3738 tv2.v_type = VAR_UNKNOWN; | |
3739 if (evaluate_const_expr1(arg, cctx, &tv2) == FAIL) | |
3740 { | |
3741 clear_tv(&tv2); | |
3742 return FAIL; | |
3743 } | |
3744 if (val) | |
3745 { | |
3746 // use the expr after "?" | |
3747 clear_tv(&tv2); | |
3748 } | |
3749 else | |
3750 { | |
3751 // use the expr after ":" | |
3752 clear_tv(tv); | |
3753 *tv = tv2; | |
3754 } | |
3755 } | |
3756 return OK; | |
3757 } | |
3758 | |
3759 static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst); | |
3760 | |
3761 /* | |
3762 * Compile code to apply '-', '+' and '!'. | 3365 * Compile code to apply '-', '+' and '!'. |
3763 */ | 3366 */ |
3764 static int | 3367 static int |
3765 compile_leader(cctx_T *cctx, char_u *start, char_u *end) | 3368 compile_leader(cctx_T *cctx, char_u *start, char_u *end) |
3766 { | 3369 { |
4468 return generate_COMPARE(cctx, type, ic); | 4071 return generate_COMPARE(cctx, type, ic); |
4469 } | 4072 } |
4470 | 4073 |
4471 return OK; | 4074 return OK; |
4472 } | 4075 } |
4076 | |
4077 static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst); | |
4473 | 4078 |
4474 /* | 4079 /* |
4475 * Compile || or &&. | 4080 * Compile || or &&. |
4476 */ | 4081 */ |
4477 static int | 4082 static int |
5725 static char_u * | 5330 static char_u * |
5726 compile_elseif(char_u *arg, cctx_T *cctx) | 5331 compile_elseif(char_u *arg, cctx_T *cctx) |
5727 { | 5332 { |
5728 char_u *p = arg; | 5333 char_u *p = arg; |
5729 garray_T *instr = &cctx->ctx_instr; | 5334 garray_T *instr = &cctx->ctx_instr; |
5335 int instr_count = instr->ga_len; | |
5730 isn_T *isn; | 5336 isn_T *isn; |
5731 scope_T *scope = cctx->ctx_scope; | 5337 scope_T *scope = cctx->ctx_scope; |
5732 typval_T tv; | 5338 ppconst_T ppconst; |
5733 | 5339 |
5734 if (scope == NULL || scope->se_type != IF_SCOPE) | 5340 if (scope == NULL || scope->se_type != IF_SCOPE) |
5735 { | 5341 { |
5736 emsg(_(e_elseif_without_if)); | 5342 emsg(_(e_elseif_without_if)); |
5737 return NULL; | 5343 return NULL; |
5747 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; | 5353 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; |
5748 isn->isn_arg.jump.jump_where = instr->ga_len; | 5354 isn->isn_arg.jump.jump_where = instr->ga_len; |
5749 } | 5355 } |
5750 | 5356 |
5751 // compile "expr"; if we know it evaluates to FALSE skip the block | 5357 // compile "expr"; if we know it evaluates to FALSE skip the block |
5752 tv.v_type = VAR_UNKNOWN; | 5358 CLEAR_FIELD(ppconst); |
5753 if (evaluate_const_expr1(&p, cctx, &tv) == OK) | 5359 if (compile_expr1(&p, cctx, &ppconst) == FAIL) |
5754 cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE; | 5360 { |
5361 clear_ppconst(&ppconst); | |
5362 return NULL; | |
5363 } | |
5364 if (instr->ga_len == instr_count && ppconst.pp_used == 1) | |
5365 { | |
5366 // The expression results in a constant. | |
5367 // TODO: how about nesting? | |
5368 cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE; | |
5369 clear_ppconst(&ppconst); | |
5370 scope->se_u.se_if.is_if_label = -1; | |
5371 } | |
5755 else | 5372 else |
5373 { | |
5374 // Not a constant, generate instructions for the expression. | |
5756 cctx->ctx_skip = MAYBE; | 5375 cctx->ctx_skip = MAYBE; |
5757 clear_tv(&tv); | 5376 if (generate_ppconst(cctx, &ppconst) == FAIL) |
5758 if (cctx->ctx_skip == MAYBE) | |
5759 { | |
5760 p = arg; | |
5761 if (compile_expr0(&p, cctx) == FAIL) | |
5762 return NULL; | 5377 return NULL; |
5763 | 5378 |
5764 // "where" is set when ":elseif", "else" or ":endif" is found | 5379 // "where" is set when ":elseif", "else" or ":endif" is found |
5765 scope->se_u.se_if.is_if_label = instr->ga_len; | 5380 scope->se_u.se_if.is_if_label = instr->ga_len; |
5766 generate_JUMP(cctx, JUMP_IF_FALSE, 0); | 5381 generate_JUMP(cctx, JUMP_IF_FALSE, 0); |
5767 } | 5382 } |
5768 else | |
5769 scope->se_u.se_if.is_if_label = -1; | |
5770 | 5383 |
5771 return p; | 5384 return p; |
5772 } | 5385 } |
5773 | 5386 |
5774 static char_u * | 5387 static char_u * |