📄 eval.c
字号:
return NULL;
}
}
/*
* types for expressions.
*/
enum exp_type
{
TYPE_UNKNOWN = 0,
TYPE_EQUAL, /* == */
TYPE_NEQUAL, /* != */
TYPE_GREATER, /* > */
TYPE_GEQUAL, /* >= */
TYPE_SMALLER, /* < */
TYPE_SEQUAL, /* <= */
TYPE_MATCH, /* =~ */
TYPE_NOMATCH /* !~ */
};
/*
* Handle zero level expression.
* This calls eval1() and handles error message and nextcmd.
* Return OK or FAIL.
*/
static int
eval0(arg, retvar, nextcmd)
char_u *arg;
VAR retvar;
char_u **nextcmd;
{
int ret;
char_u *p;
p = skipwhite(arg);
ret = eval1(&p, retvar);
if (ret == FAIL || !ends_excmd(*p))
{
if (ret != FAIL)
clear_var(retvar);
EMSG2(e_invexpr2, arg);
ret = FAIL;
}
if (nextcmd != NULL)
*nextcmd = check_nextcmd(p);
return ret;
}
/*
* Handle first level expression:
* expr2 || expr2 || expr2 logical OR
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Return OK or FAIL.
*/
static int
eval1(arg, retvar)
char_u **arg;
VAR retvar;
{
var var2;
long n1, n2;
/*
* Get the first variable.
*/
if (eval2(arg, retvar) == FAIL)
return FAIL;
/*
* Repeat until there is no following "||".
*/
while ((*arg)[0] == '|' && (*arg)[1] == '|')
{
n1 = get_var_number(retvar);
clear_var(retvar);
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
if (eval2(arg, &var2) == FAIL)
return FAIL;
/*
* Compute the result.
*/
n2 = get_var_number(&var2);
clear_var(&var2);
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = (n1 || n2);
}
return OK;
}
/*
* Handle second level expression:
* expr3 && expr3 && expr3 logical AND
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Return OK or FAIL.
*/
static int
eval2(arg, retvar)
char_u **arg;
VAR retvar;
{
var var2;
long n1, n2;
/*
* Get the first variable.
*/
if (eval3(arg, retvar) == FAIL)
return FAIL;
/*
* Repeat until there is no following "&&".
*/
while ((*arg)[0] == '&' && (*arg)[1] == '&')
{
n1 = get_var_number(retvar);
clear_var(retvar);
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
if (eval3(arg, &var2) == FAIL)
return FAIL;
/*
* Compute the result.
*/
n2 = get_var_number(&var2);
clear_var(&var2);
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = (n1 && n2);
}
return OK;
}
/*
* Handle third level expression:
* var1 == var2
* var1 =~ var2
* var1 != var2
* var1 !~ var2
* var1 > var2
* var1 >= var2
* var1 < var2
* var1 <= var2
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Return OK or FAIL.
*/
static int
eval3(arg, retvar)
char_u **arg;
VAR retvar;
{
var var2;
char_u *p;
int i = 0;
enum exp_type type = TYPE_UNKNOWN;
int len = 2;
long n1 = FALSE, n2;
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
vim_regexp *prog;
/*
* Get the first variable.
*/
if (eval4(arg, retvar) == FAIL)
return FAIL;
p = *arg;
switch (p[0])
{
case '=': if (p[1] == '=')
type = TYPE_EQUAL;
else if (p[1] == '~')
type = TYPE_MATCH;
break;
case '!': if (p[1] == '=')
type = TYPE_NEQUAL;
else if (p[1] == '~')
type = TYPE_NOMATCH;
break;
case '>': if (p[1] != '=')
{
type = TYPE_GREATER;
len = 1;
}
else
type = TYPE_GEQUAL;
break;
case '<': if (p[1] != '=')
{
type = TYPE_SMALLER;
len = 1;
}
else
type = TYPE_SEQUAL;
break;
}
/*
* If there is a comparitive operator, use it.
*/
if (type != TYPE_UNKNOWN)
{
/*
* Get the second variable.
*/
*arg = skipwhite(p + len);
if (eval4(arg, &var2) == FAIL)
{
clear_var(retvar);
return FAIL;
}
/*
* If one of the two variables is a number, compare as a number.
* When using "=~" or "!~", always compare as string.
*/
if ((retvar->var_type == VAR_NUMBER || var2.var_type == VAR_NUMBER)
&& type != TYPE_MATCH && type != TYPE_NOMATCH)
{
n1 = get_var_number(retvar);
n2 = get_var_number(&var2);
switch (type)
{
case TYPE_EQUAL: n1 = (n1 == n2); break;
case TYPE_NEQUAL: n1 = (n1 != n2); break;
case TYPE_GREATER: n1 = (n1 > n2); break;
case TYPE_GEQUAL: n1 = (n1 >= n2); break;
case TYPE_SMALLER: n1 = (n1 < n2); break;
case TYPE_SEQUAL: n1 = (n1 <= n2); break;
case TYPE_UNKNOWN:
case TYPE_MATCH:
case TYPE_NOMATCH: break; /* avoid gcc warning */
}
}
else
{
s1 = get_var_string_buf(retvar, buf1);
s2 = get_var_string_buf(&var2, buf2);
if (type != TYPE_MATCH && type != TYPE_NOMATCH)
i = STRCMP(s1, s2);
switch (type)
{
case TYPE_EQUAL: n1 = (i == 0); break;
case TYPE_NEQUAL: n1 = (i != 0); break;
case TYPE_GREATER: n1 = (i > 0); break;
case TYPE_GEQUAL: n1 = (i >= 0); break;
case TYPE_SMALLER: n1 = (i < 0); break;
case TYPE_SEQUAL: n1 = (i <= 0); break;
case TYPE_MATCH:
case TYPE_NOMATCH: reg_ic = p_ic;
prog = vim_regcomp(s2, TRUE);
if (prog != NULL)
{
n1 = vim_regexec(prog, s1, TRUE);
vim_free(prog);
if (type == TYPE_NOMATCH)
n1 = !n1;
}
break;
case TYPE_UNKNOWN: break; /* avoid gcc warning */
}
}
clear_var(retvar);
clear_var(&var2);
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = n1;
}
return OK;
}
/*
* Handle fourth level expression:
* + number addition
* - number subtraction
* . string concatenation
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Return OK or FAIL.
*/
static int
eval4(arg, retvar)
char_u **arg;
VAR retvar;
{
var var2;
int op;
long n1, n2;
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
char_u *p;
/*
* Get the first variable.
*/
if (eval5(arg, retvar) == FAIL)
return FAIL;
/*
* Repeat computing, until no '+', '-' or '.' is following.
*/
for (;;)
{
op = **arg;
if (op != '+' && op != '-' && op != '.')
break;
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
if (eval5(arg, &var2) == FAIL)
{
clear_var(retvar);
return FAIL;
}
/*
* Compute the result.
*/
if (op == '.')
{
s1 = get_var_string_buf(retvar, buf1);
s2 = get_var_string_buf(&var2, buf2);
p = alloc((unsigned)(STRLEN(s1) + STRLEN(s2) + 1));
if (p != NULL)
{
STRCPY(p, s1);
STRCAT(p, s2);
}
clear_var(retvar);
retvar->var_type = VAR_STRING;
retvar->var_val.var_string = p;
}
else
{
n1 = get_var_number(retvar);
n2 = get_var_number(&var2);
clear_var(retvar);
if (op == '+')
n1 = n1 + n2;
else
n1 = n1 - n2;
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = n1;
}
clear_var(&var2);
}
return OK;
}
/*
* Handle fifth level expression:
* * number multiplication
* / number division
* % number modulo
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Return OK or FAIL.
*/
static int
eval5(arg, retvar)
char_u **arg;
VAR retvar;
{
var var2;
int op;
long n1, n2;
/*
* Get the first variable.
*/
if (eval6(arg, retvar) == FAIL)
return FAIL;
/*
* Repeat computing, until no '*', '/' or '%' is following.
*/
for (;;)
{
op = **arg;
if (op != '*' && op != '/' && op != '%')
break;
n1 = get_var_number(retvar);
clear_var(retvar);
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
if (eval6(arg, &var2) == FAIL)
return FAIL;
n2 = get_var_number(&var2);
clear_var(&var2);
/*
* Compute the result.
*/
if (op == '*')
n1 = n1 * n2;
else if (op == '/')
{
if (n2 == 0) /* give an error message? */
n1 = 0x7fffffff;
else
n1 = n1 / n2;
}
else
{
if (n2 == 0) /* give an error message? */
n1 = 0;
else
n1 = n1 % n2;
}
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = n1;
}
return OK;
}
/*
* Handle sixth level expression:
* number number constant
* "string" string contstant
* *option-name option value
* @r register contents
* identifier variable value
* $VAR environment variable
* (expression) nested expression
*
* Also handle:
* ! in front logical NOT
* - in front unary minus
* trailing [] subscript in String
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Return OK or FAIL.
*/
static int
eval6(arg, retvar)
char_u **arg;
VAR retvar;
{
var var2;
long n;
int len;
char_u *s;
int val;
char_u *start_leader, *end_leader;
int ret = OK;
/*
* Skip '!' and '-' characters. They are handled later.
*/
start_leader = *arg;
while (**arg == '!' || **arg == '-')
*arg = skipwhite(*arg + 1);
end_leader = *arg;
switch (**arg)
{
/*
* Number constant.
*/
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
retvar->var_type = VAR_NUMBER;
vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL);
retvar->var_val.var_number = n;
*arg += len;
break;
/*
* String constant: "string".
*/
case '\"': ret = get_string_var(arg, retvar);
break;
/*
* Literal string constant: 'string'.
*/
case '\'': ret = get_lit_string_var(arg, retvar);
break;
/*
* Option value: &name
*/
case '&': ret = get_option_var(arg, retvar);
break;
/*
* Environment variable: $VAR.
*/
case '$': ret = get_env_var(arg, retvar);
break;
/*
* Register contents: @r.
*/
case '@': retvar->var_type = VAR_STRING;
retvar->var_val.var_string = get_reg_contents(*++*arg);
if (**arg != NUL)
++*arg;
break;
/*
* nested expression: (expression).
*/
case '(': *arg = skipwhite(*arg + 1);
ret = eval1(arg, retvar); /* recursive! */
if (**arg != ')')
EMSG("Missing ')'");
else
++*arg;
break;
/*
* Must be a variable or function name then.
*/
default: s = *arg;
len = get_id_len(arg);
if (len)
{
if (**arg == '(') /* recursive! */
ret = get_func_var(s, len, retvar, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len);
else
ret = get_var_var(s, len, retvar);
}
else
ret = FAIL;
break;
}
*arg = skipwhite(*arg);
/*
* Handle expr[expr] subscript.
*/
if (**arg == '[' && ret == OK)
{
/*
* Get the variable from inside the [].
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, &var2) == FAIL) /* recursive! */
{
clear_var(retvar);
return FAIL;
}
n = get_var_number(&var2);
clear_var(&var2);
/* Check for the ']'. */
if (**arg != ']')
{
EMSG("Missing ']'");
clear_var(retvar);
return FAIL;
}
/*
* The resulting variable is a string of a single character.
* If the index is too big or negative, the result is empty.
*/
s = get_var_string(retvar);
if (n >= (long)STRLEN(s) || n < 0)
s = NULL;
else
s = vim_strnsave(s + n, 1);
clear_var(retvar);
retvar->var_type = VAR_STRING;
retvar->var_val.var_string = s;
*arg = skipwhite(*arg + 1); /* skip the ']' */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -