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 *