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: