Mercurial > vim
comparison src/vim9compile.c @ 19253:a8d2d3c8f0b3 v8.2.0185
patch 8.2.0185: Vim9 script: cannot use "if has()" to skip lines
Commit: https://github.com/vim/vim/commit/a259d8d30bc289764925fc42db1dbe774f0bb3f8
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri Jan 31 20:10:50 2020 +0100
patch 8.2.0185: Vim9 script: cannot use "if has()" to skip lines
Problem: Vim9 script: cannot use "if has()" to skip lines.
Solution: Evaluate constant expression at runtime.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 31 Jan 2020 20:15:05 +0100 |
parents | d776967d0f0d |
children | 5aab9c306181 |
comparison
equal
deleted
inserted
replaced
19252:2f4b2122c7d1 | 19253:a8d2d3c8f0b3 |
---|---|
113 garray_T ctx_locals; // currently visible local variables | 113 garray_T ctx_locals; // currently visible local variables |
114 int ctx_max_local; // maximum number of locals at one time | 114 int ctx_max_local; // maximum number of locals at one time |
115 | 115 |
116 garray_T ctx_imports; // imported items | 116 garray_T ctx_imports; // imported items |
117 | 117 |
118 int ctx_skip; // when TRUE skip commands, when FALSE skip | |
119 // commands after "else" | |
118 scope_T *ctx_scope; // current scope, NULL at toplevel | 120 scope_T *ctx_scope; // current scope, NULL at toplevel |
119 | 121 |
120 garray_T ctx_type_stack; // type of each item on the stack | 122 garray_T ctx_type_stack; // type of each item on the stack |
121 garray_T *ctx_type_list; // space for adding types | 123 garray_T *ctx_type_list; // space for adding types |
122 }; | 124 }; |
3457 scope->se_local_count = cctx->ctx_locals.ga_len; | 3459 scope->se_local_count = cctx->ctx_locals.ga_len; |
3458 return scope; | 3460 return scope; |
3459 } | 3461 } |
3460 | 3462 |
3461 /* | 3463 /* |
3464 * Evaluate an expression that is a constant: has(arg) | |
3465 * Return FAIL if the expression is not a constant. | |
3466 */ | |
3467 static int | |
3468 evaluate_const_expr4(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv) | |
3469 { | |
3470 typval_T argvars[2]; | |
3471 | |
3472 if (STRNCMP("has(", *arg, 4) != 0) | |
3473 return FAIL; | |
3474 *arg = skipwhite(*arg + 4); | |
3475 | |
3476 if (**arg == '"') | |
3477 { | |
3478 if (get_string_tv(arg, tv, TRUE) == FAIL) | |
3479 return FAIL; | |
3480 } | |
3481 else if (**arg == '\'') | |
3482 { | |
3483 if (get_lit_string_tv(arg, tv, TRUE) == FAIL) | |
3484 return FAIL; | |
3485 } | |
3486 else | |
3487 return FAIL; | |
3488 | |
3489 *arg = skipwhite(*arg); | |
3490 if (**arg != ')') | |
3491 return FAIL; | |
3492 *arg = skipwhite(*arg + 1); | |
3493 | |
3494 argvars[0] = *tv; | |
3495 argvars[1].v_type = VAR_UNKNOWN; | |
3496 tv->v_type = VAR_NUMBER; | |
3497 tv->vval.v_number = 0; | |
3498 f_has(argvars, tv); | |
3499 clear_tv(&argvars[0]); | |
3500 | |
3501 return OK; | |
3502 } | |
3503 | |
3504 static int evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv); | |
3505 | |
3506 /* | |
3507 * Compile constant || or &&. | |
3508 */ | |
3509 static int | |
3510 evaluate_const_and_or(char_u **arg, cctx_T *cctx, char *op, typval_T *tv) | |
3511 { | |
3512 char_u *p = skipwhite(*arg); | |
3513 int opchar = *op; | |
3514 | |
3515 if (p[0] == opchar && p[1] == opchar) | |
3516 { | |
3517 int val = tv2bool(tv); | |
3518 | |
3519 /* | |
3520 * Repeat until there is no following "||" or "&&" | |
3521 */ | |
3522 while (p[0] == opchar && p[1] == opchar) | |
3523 { | |
3524 typval_T tv2; | |
3525 | |
3526 if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[2])) | |
3527 return FAIL; | |
3528 | |
3529 // eval the next expression | |
3530 *arg = skipwhite(p + 2); | |
3531 tv2.v_type = VAR_UNKNOWN; | |
3532 if ((opchar == '|' ? evaluate_const_expr3(arg, cctx, &tv2) | |
3533 : evaluate_const_expr4(arg, cctx, &tv2)) == FAIL) | |
3534 { | |
3535 clear_tv(&tv2); | |
3536 return FAIL; | |
3537 } | |
3538 if ((opchar == '&') == val) | |
3539 { | |
3540 // false || tv2 or true && tv2: use tv2 | |
3541 clear_tv(tv); | |
3542 *tv = tv2; | |
3543 val = tv2bool(tv); | |
3544 } | |
3545 else | |
3546 clear_tv(&tv2); | |
3547 p = skipwhite(*arg); | |
3548 } | |
3549 } | |
3550 | |
3551 return OK; | |
3552 } | |
3553 | |
3554 /* | |
3555 * Evaluate an expression that is a constant: expr4 && expr4 && expr4 | |
3556 * Return FAIL if the expression is not a constant. | |
3557 */ | |
3558 static int | |
3559 evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3560 { | |
3561 // evaluate the first expression | |
3562 if (evaluate_const_expr4(arg, cctx, tv) == FAIL) | |
3563 return FAIL; | |
3564 | |
3565 // || and && work almost the same | |
3566 return evaluate_const_and_or(arg, cctx, "&&", tv); | |
3567 } | |
3568 | |
3569 /* | |
3570 * Evaluate an expression that is a constant: expr3 || expr3 || expr3 | |
3571 * Return FAIL if the expression is not a constant. | |
3572 */ | |
3573 static int | |
3574 evaluate_const_expr2(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3575 { | |
3576 // evaluate the first expression | |
3577 if (evaluate_const_expr3(arg, cctx, tv) == FAIL) | |
3578 return FAIL; | |
3579 | |
3580 // || and && work almost the same | |
3581 return evaluate_const_and_or(arg, cctx, "||", tv); | |
3582 } | |
3583 | |
3584 /* | |
3585 * Evaluate an expression that is a constant: expr2 ? expr1 : expr1 | |
3586 * E.g. for "has('feature')". | |
3587 * This does not produce error messages. "tv" should be cleared afterwards. | |
3588 * Return FAIL if the expression is not a constant. | |
3589 */ | |
3590 static int | |
3591 evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv) | |
3592 { | |
3593 char_u *p; | |
3594 | |
3595 // evaluate the first expression | |
3596 if (evaluate_const_expr2(arg, cctx, tv) == FAIL) | |
3597 return FAIL; | |
3598 | |
3599 p = skipwhite(*arg); | |
3600 if (*p == '?') | |
3601 { | |
3602 int val = tv2bool(tv); | |
3603 typval_T tv2; | |
3604 | |
3605 if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) | |
3606 return FAIL; | |
3607 | |
3608 // evaluate the second expression; any type is accepted | |
3609 clear_tv(tv); | |
3610 *arg = skipwhite(p + 1); | |
3611 if (evaluate_const_expr1(arg, cctx, tv) == FAIL) | |
3612 return FAIL; | |
3613 | |
3614 // Check for the ":". | |
3615 p = skipwhite(*arg); | |
3616 if (*p != ':' || !VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) | |
3617 return FAIL; | |
3618 | |
3619 // evaluate the third expression | |
3620 *arg = skipwhite(p + 1); | |
3621 tv2.v_type = VAR_UNKNOWN; | |
3622 if (evaluate_const_expr1(arg, cctx, &tv2) == FAIL) | |
3623 { | |
3624 clear_tv(&tv2); | |
3625 return FAIL; | |
3626 } | |
3627 if (val) | |
3628 { | |
3629 // use the expr after "?" | |
3630 clear_tv(&tv2); | |
3631 } | |
3632 else | |
3633 { | |
3634 // use the expr after ":" | |
3635 clear_tv(tv); | |
3636 *tv = tv2; | |
3637 } | |
3638 } | |
3639 return OK; | |
3640 } | |
3641 | |
3642 /* | |
3462 * compile "if expr" | 3643 * compile "if expr" |
3463 * | 3644 * |
3464 * "if expr" Produces instructions: | 3645 * "if expr" Produces instructions: |
3465 * EVAL expr Push result of "expr" | 3646 * EVAL expr Push result of "expr" |
3466 * JUMP_IF_FALSE end | 3647 * JUMP_IF_FALSE end |
3494 compile_if(char_u *arg, cctx_T *cctx) | 3675 compile_if(char_u *arg, cctx_T *cctx) |
3495 { | 3676 { |
3496 char_u *p = arg; | 3677 char_u *p = arg; |
3497 garray_T *instr = &cctx->ctx_instr; | 3678 garray_T *instr = &cctx->ctx_instr; |
3498 scope_T *scope; | 3679 scope_T *scope; |
3499 | 3680 typval_T tv; |
3500 // compile "expr" | 3681 |
3501 if (compile_expr1(&p, cctx) == FAIL) | 3682 // compile "expr"; if we know it evaluates to FALSE skip the block |
3502 return NULL; | 3683 tv.v_type = VAR_UNKNOWN; |
3684 if (evaluate_const_expr1(&p, cctx, &tv) == OK) | |
3685 cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE; | |
3686 else | |
3687 cctx->ctx_skip = MAYBE; | |
3688 clear_tv(&tv); | |
3689 if (cctx->ctx_skip == MAYBE) | |
3690 { | |
3691 p = arg; | |
3692 if (compile_expr1(&p, cctx) == FAIL) | |
3693 return NULL; | |
3694 } | |
3503 | 3695 |
3504 scope = new_scope(cctx, IF_SCOPE); | 3696 scope = new_scope(cctx, IF_SCOPE); |
3505 if (scope == NULL) | 3697 if (scope == NULL) |
3506 return NULL; | 3698 return NULL; |
3507 | 3699 |
3508 // "where" is set when ":elseif", "else" or ":endif" is found | 3700 if (cctx->ctx_skip == MAYBE) |
3509 scope->se_u.se_if.is_if_label = instr->ga_len; | 3701 { |
3510 generate_JUMP(cctx, JUMP_IF_FALSE, 0); | 3702 // "where" is set when ":elseif", "else" or ":endif" is found |
3703 scope->se_u.se_if.is_if_label = instr->ga_len; | |
3704 generate_JUMP(cctx, JUMP_IF_FALSE, 0); | |
3705 } | |
3706 else | |
3707 scope->se_u.se_if.is_if_label = -1; | |
3511 | 3708 |
3512 return p; | 3709 return p; |
3513 } | 3710 } |
3514 | 3711 |
3515 static char_u * | 3712 static char_u * |
3517 { | 3714 { |
3518 char_u *p = arg; | 3715 char_u *p = arg; |
3519 garray_T *instr = &cctx->ctx_instr; | 3716 garray_T *instr = &cctx->ctx_instr; |
3520 isn_T *isn; | 3717 isn_T *isn; |
3521 scope_T *scope = cctx->ctx_scope; | 3718 scope_T *scope = cctx->ctx_scope; |
3719 typval_T tv; | |
3522 | 3720 |
3523 if (scope == NULL || scope->se_type != IF_SCOPE) | 3721 if (scope == NULL || scope->se_type != IF_SCOPE) |
3524 { | 3722 { |
3525 emsg(_(e_elseif_without_if)); | 3723 emsg(_(e_elseif_without_if)); |
3526 return NULL; | 3724 return NULL; |
3527 } | 3725 } |
3528 cctx->ctx_locals.ga_len = scope->se_local_count; | 3726 cctx->ctx_locals.ga_len = scope->se_local_count; |
3529 | 3727 |
3530 // jump from previous block to the end | 3728 if (cctx->ctx_skip != TRUE) |
3531 if (compile_jump_to_end(&scope->se_u.se_if.is_end_label, | 3729 { |
3730 if (compile_jump_to_end(&scope->se_u.se_if.is_end_label, | |
3532 JUMP_ALWAYS, cctx) == FAIL) | 3731 JUMP_ALWAYS, cctx) == FAIL) |
3533 return NULL; | 3732 return NULL; |
3534 | 3733 // previous "if" or "elseif" jumps here |
3535 // previous "if" or "elseif" jumps here | 3734 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; |
3536 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; | 3735 isn->isn_arg.jump.jump_where = instr->ga_len; |
3537 isn->isn_arg.jump.jump_where = instr->ga_len; | 3736 } |
3538 | 3737 |
3539 // compile "expr" | 3738 // compile "expr"; if we know it evaluates to FALSE skip the block |
3540 if (compile_expr1(&p, cctx) == FAIL) | 3739 tv.v_type = VAR_UNKNOWN; |
3541 return NULL; | 3740 if (evaluate_const_expr1(&p, cctx, &tv) == OK) |
3542 | 3741 cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE; |
3543 // "where" is set when ":elseif", "else" or ":endif" is found | 3742 else |
3544 scope->se_u.se_if.is_if_label = instr->ga_len; | 3743 cctx->ctx_skip = MAYBE; |
3545 generate_JUMP(cctx, JUMP_IF_FALSE, 0); | 3744 clear_tv(&tv); |
3745 if (cctx->ctx_skip == MAYBE) | |
3746 { | |
3747 p = arg; | |
3748 if (compile_expr1(&p, cctx) == FAIL) | |
3749 return NULL; | |
3750 | |
3751 // "where" is set when ":elseif", "else" or ":endif" is found | |
3752 scope->se_u.se_if.is_if_label = instr->ga_len; | |
3753 generate_JUMP(cctx, JUMP_IF_FALSE, 0); | |
3754 } | |
3755 else | |
3756 scope->se_u.se_if.is_if_label = -1; | |
3546 | 3757 |
3547 return p; | 3758 return p; |
3548 } | 3759 } |
3549 | 3760 |
3550 static char_u * | 3761 static char_u * |
3560 emsg(_(e_else_without_if)); | 3771 emsg(_(e_else_without_if)); |
3561 return NULL; | 3772 return NULL; |
3562 } | 3773 } |
3563 cctx->ctx_locals.ga_len = scope->se_local_count; | 3774 cctx->ctx_locals.ga_len = scope->se_local_count; |
3564 | 3775 |
3565 // jump from previous block to the end | 3776 // jump from previous block to the end, unless the else block is empty |
3566 if (compile_jump_to_end(&scope->se_u.se_if.is_end_label, | 3777 if (cctx->ctx_skip == MAYBE) |
3778 { | |
3779 if (compile_jump_to_end(&scope->se_u.se_if.is_end_label, | |
3567 JUMP_ALWAYS, cctx) == FAIL) | 3780 JUMP_ALWAYS, cctx) == FAIL) |
3568 return NULL; | 3781 return NULL; |
3569 | 3782 } |
3570 // previous "if" or "elseif" jumps here | 3783 |
3571 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; | 3784 if (cctx->ctx_skip != TRUE) |
3572 isn->isn_arg.jump.jump_where = instr->ga_len; | 3785 { |
3786 if (scope->se_u.se_if.is_if_label >= 0) | |
3787 { | |
3788 // previous "if" or "elseif" jumps here | |
3789 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; | |
3790 isn->isn_arg.jump.jump_where = instr->ga_len; | |
3791 } | |
3792 } | |
3793 | |
3794 if (cctx->ctx_skip != MAYBE) | |
3795 cctx->ctx_skip = !cctx->ctx_skip; | |
3573 | 3796 |
3574 return p; | 3797 return p; |
3575 } | 3798 } |
3576 | 3799 |
3577 static char_u * | 3800 static char_u * |
3589 } | 3812 } |
3590 ifscope = &scope->se_u.se_if; | 3813 ifscope = &scope->se_u.se_if; |
3591 cctx->ctx_scope = scope->se_outer; | 3814 cctx->ctx_scope = scope->se_outer; |
3592 cctx->ctx_locals.ga_len = scope->se_local_count; | 3815 cctx->ctx_locals.ga_len = scope->se_local_count; |
3593 | 3816 |
3594 // previous "if" or "elseif" jumps here | 3817 if (scope->se_u.se_if.is_if_label >= 0) |
3595 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; | 3818 { |
3596 isn->isn_arg.jump.jump_where = instr->ga_len; | 3819 // previous "if" or "elseif" jumps here |
3597 | 3820 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; |
3821 isn->isn_arg.jump.jump_where = instr->ga_len; | |
3822 } | |
3598 // Fill in the "end" label in jumps at the end of the blocks. | 3823 // Fill in the "end" label in jumps at the end of the blocks. |
3599 compile_fill_jump_to_end(&ifscope->is_end_label, cctx); | 3824 compile_fill_jump_to_end(&ifscope->is_end_label, cctx); |
3825 cctx->ctx_skip = FALSE; | |
3600 | 3826 |
3601 vim_free(scope); | 3827 vim_free(scope); |
3602 return arg; | 3828 return arg; |
3603 } | 3829 } |
3604 | 3830 |
4324 ea.cmd = skip_range(ea.cmd, NULL); | 4550 ea.cmd = skip_range(ea.cmd, NULL); |
4325 p = find_ex_command(&ea, NULL, lookup_local, &cctx); | 4551 p = find_ex_command(&ea, NULL, lookup_local, &cctx); |
4326 | 4552 |
4327 if (p == ea.cmd && ea.cmdidx != CMD_SIZE) | 4553 if (p == ea.cmd && ea.cmdidx != CMD_SIZE) |
4328 { | 4554 { |
4555 if (cctx.ctx_skip == TRUE) | |
4556 { | |
4557 line += STRLEN(line); | |
4558 continue; | |
4559 } | |
4560 | |
4329 // Expression or function call. | 4561 // Expression or function call. |
4330 if (ea.cmdidx == CMD_eval) | 4562 if (ea.cmdidx == CMD_eval) |
4331 { | 4563 { |
4332 p = ea.cmd; | 4564 p = ea.cmd; |
4333 if (compile_expr1(&p, &cctx) == FAIL) | 4565 if (compile_expr1(&p, &cctx) == FAIL) |
4348 iemsg("Command from find_ex_command() not handled"); | 4580 iemsg("Command from find_ex_command() not handled"); |
4349 goto erret; | 4581 goto erret; |
4350 } | 4582 } |
4351 | 4583 |
4352 p = skipwhite(p); | 4584 p = skipwhite(p); |
4585 | |
4586 if (cctx.ctx_skip == TRUE | |
4587 && ea.cmdidx != CMD_elseif | |
4588 && ea.cmdidx != CMD_else | |
4589 && ea.cmdidx != CMD_endif) | |
4590 { | |
4591 line += STRLEN(line); | |
4592 continue; | |
4593 } | |
4353 | 4594 |
4354 switch (ea.cmdidx) | 4595 switch (ea.cmdidx) |
4355 { | 4596 { |
4356 case CMD_def: | 4597 case CMD_def: |
4357 case CMD_function: | 4598 case CMD_function: |