文章归档

vim如何处理三元表达式

关于vim script的条件表达式,描述有点问题,在runtime/doc/usr_41.txt文档里:

Borrowed from the C language is the conditional expression:

	a ? b : c

If "a" evaluates to true "b" is used, otherwise "c" is used.  Example: >

	:let i = 4
	:echo i > 5 ? "i is big" : "i is small"
<	i is small ~  The three parts of the constructs are always
evaluated first, thus you could see it work as:
	(a) ? (b) : (c)

有问题那句我红色标出来了,它的意思是说,在计算一个条件表达式的值的时候三部分都是会先被计算的。也就是说,先计算(a)的值,(b)的值,和(c)的值,然后根据(a)是否为true,选择表达式最终的值。
这个做法看起来很有问题,我确实想不出为什么要这么做,至少c这些语言大多编译器的实现,都是最终转化为一个goto语句吧。
如果这么做是可行的,那么是不是说:

	:let a = 3 < 5 ? 3 - 1 : getchar()

即使最终的结果是part2,但是getchar()也会被执行?

事实上不是,看了一下vim script的解释器的代码,如下:

/*
 * Handle top level expression:
 * expr1 ? expr0 : expr0
 */
static int
eval1(arg, rettv, evaluate)
   char_u **arg;
   typval_T *rettv;
   int evaluate;
{
  ....
  /*
   * Get the first variable.  process part1
   */
  if (eval2(arg, rettv, evaluate) == FAIL)
    return FAIL;

  if ((*arg)[0] == '?') {
    result = FALSE;
    if (evaluate) {
      int error = FALSE;

      if (get_tv_number_chk(rettv, &error) != 0)
        result = TRUE;
      clear_tv(rettv);
      if (error)
        return FAIL;
    }

    /*
     * Get the second variable.
     */
    *arg = skipwhite(*arg + 1);
    // process part2
    if (eval1(arg, rettv, evaluate && result) == FAIL) /* recursive! */
      return FAIL;

    /*
     * Check for the ":".
     */
    if ((*arg)[0] != ':') {
      EMSG(_("E109: Missing ':' after '?'"));
      if (evaluate && result)
        clear_tv(rettv);
      return FAIL;
    }

    /*
     * Get the third variable.
     */
    *arg = skipwhite(*arg + 1);
    // process part3
    if (eval1(arg, &var2, evaluate && !result) == FAIL) /* recursive! */ {
      if (evaluate && result)
        clear_tv(rettv);
      return FAIL;
    }

    // here
    if (evaluate && !result)
      *rettv = var2;
  }

  return OK;
}

从这个逻辑上看来,文档上的描述似乎是正确的,part1的值会先被计算,然后依次计算part2和part3的值,如果part1的值不为真,会将part3的值返回,否则返回part2的值,见here
但如果继续往下跟踪,就会发现,表面上看起来是三部分都会evaluate的,但实际上不是,很多真正的动作是会依靠evaluate这个flag来完成。如果part1为true,才会执行part2的真正compute动作,而part3是不会执行的。反过来也是。否则,文档上所描述的evaluate只是简单的parse。

比如,如果某部分是个+/-表达式的话:

/*
 * Handle fourth level expression:
 * + number addition
 * - number subtraction
 * . string concatenation
 *
 * Return OK or FAIL.
 */
static int
eval5(arg, rettv, evaluate)
   char_u **arg;
   typval_T *rettv;
   int evaluate;
{
  ....
  /*
   * Get the first variable.
   */
  if (eval6(arg, rettv, evaluate, FALSE) == FAIL)
    return FAIL;

  /*
   * Repeat computing, until no '+', '-' or '.' is following.
   */
  for (;;) {
    ....
    /*
     * Get the second variable.
     */
    *arg = skipwhite(*arg + 1);
    if (eval6(arg, &amp;var2, evaluate, op == '.') == FAIL) {
      clear_tv(rettv);
      return FAIL;
    }

    if (evaluate) {
      /*
       * Compute the result.
       */
      ...
    }
  }
  return OK;
}

同理,function也是,故getchar()总是不会被执行的。
vim那个文档确实有问题。

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>