13. ARITHMETIC EXPRESSIONS
Elvis can evaluate expressions involving numbers, strings, and boolean values,
using a C-like syntax.
These are used in several EX commands, one VI command, and a couple of other
situations.
There are two syntaxes.
The normal syntax is extremely similar to C, and is used in circumstances
where you probably would never use a literal value, such as for the
:if command.
The simpler syntax makes literal values easier to enter, while still
making the full power of the expression evaluator available if you need it.
13.1 Normal (C-like) Syntax
The :calculate command uses the normal syntax
and displays the results.
We'll use it for most of the examples in this section.
The normal syntax is intended to resemble the syntax of the C programming
language very closely.
You can't define your own functions or use flow-control constructs though;
you can only use expressions.
In traditional C documentation, these would be called "rvalues."
Basically that means you can use literal values, option names, operators,
parentheses, and some built-in functions.
13.1.1 Primary expressions
Literals can be given in any of the following formats:
- "text"
- Any text in double-quotes is taken literally.
The usual C escapes are supported:
\b
, \E
(uppercase, representing the Esc character), \f
,
\n
, \r
, and \t
.
Also, you can use \\
for a literal backslash character, or
\"
for a literal double-quote character within a string.
- /regexp/
- This is a special notation for passing a regular expression as the
argument to a function such as current().
In any other context, the leading '/' character would be taken as a division
operator, but as a function argument you can use
/
regexp/
to pass the regular expression
text.
Note that the regular expression is passed as a string;
it is equivelent to "/
regexp"
.
- \$
\(
\)
\\
- You can use a backslash to quote a single dollar sign, parenthesis, or
backslash as though it was a string of length 1.
This was done mostly for the benefit of the simpler syntax,
where these four character are normally the only ones which have any special
interpretation.
- digits
- Any word which contains only digits will be taken as a literal value.
Generally this value will be interpreted as a number,
but internally the expression evaluator always stores values as strings.
Some operators look at their arguments and act differently depending
on whether those strings happen to look like numbers or Boolean values.
- 0octaldigits
0xhexdigits
'character'
- Octal, hex, and character constants can be used in expressions.
These are converted to decimal when they are parsed, before they are passed to any operator or function.
Passing an octal, hex, or character constant therefore is exactly like
passing the equivalent decimal number.
Elvis supports escapes as character constants:
'\0', '\b', '\E', '\f', '\n', '\r', and '\t'.
- true
false
- These can be used as Boolean literals.
Technically, they are implemented via options (as described below) named
true and
false.
All of the boolean operators accept "false", "0", "", or the value of the
false option as
Boolean false values, and
anything else as a Boolean true value.
The following examples produce exactly identical results.
:calc "8"
8
:calc 8
8
:calc 010
8
:calc 0x8
8
:calc '\b'
8
You can also use option names in elvis the same way you would use variable
names in C.
:calc list
false
:calc scroll
12
:calc display
normal
Additionally, a dollar sign followed by the name of an environment variable
is replaced by the value of that environment variable. If there is no such
environment variable, then elvis will act as though it exists and has a
null value.
In some circumstances, you can use a dollar sign followed by a digit
to access special arguments.
This is used in error messages and also in the values of a few options,
as described in section 13.6.
These special arguments can only be supplied by elvis' internal code,
and it only supplies them in a few special circumstances so you can't use
them in :calculate, for example.
13.1.2 Operators
The following operators are available.
When passed integer values, these operators act like their C counterparts.
When passed string values, most of them concatenate
their arguments with the operator name in between, but some of them
do something that is useful for strings,
as described below.
Items at the top of this list have a higher precedence than those lower down.
- (no operator)
- Any two expressions placed side-by-side with no operator between them will
be concatenated as strings. C does this for literal strings, but elvis does
it for anything.
- ~
- Perform a bitwise NOT operation on the argument, if it is a number.
- !
- Return true if the argument is false
and vice versa.
- * / %
- The usual arithmetic operators. (% is the modulo operator.)
Also, the / operator can be used to combine a directory name and a file name,
to form an absolute pathname.
Here are some examples showing how this works in DOS:
:set dir home
directory=C:\temp home=C:\
:calc dir/"tempfile"
C:\temp\tempfile
:calc home/"elvis.rc"
C:\elvis.rc
- + -
- The usual arithmetic operators.
Note that there is no special unary - sign; the minus sign serves double-duty.
Because C normally gives the unary - sign a higher precedence than
other operators and elvis doesn't, you may occasionally need to enclose
negated values in parentheses to achieve the same effect.
- << >>
- For integers these operators perform bitwise shifting, exactly like C.
However, if the left argument is a string and the right argument is a number
then elvis will pad or truncate the string to make its length match the
number argument.
<< pads/truncates on the right, and >> pads/truncates on the left.
:calc \[("port" << 6)\]
[port ]
:calc \[("starboard" >> 6)\]
[rboard]
- < <= > >= == !=
- Compare the arguments and return true if the comparison
holds, and false otherwise.
If both arguments look like numbers, then they will be compared as numbers;
otherwise they will be compared as strings.
- &
- Bitwise AND of the arguments, if they're numbers.
- ^
- Bitwise XOR of the arguments, if they're numbers.
- |
- Bitwise OR of the arguments, if they're numbers.
- &&
- Returns false if either argument is one of the
four false string values, and true otherwise.
Both arguments are always evaluated; this is different from C, where the
right argument is only evaluated if the left argument is true.
- ||
- Returns false if both arguments are one of the
four false string values, and true otherwise.
Both arguments are always evaluated; this is different from C, where the
right argument is only evaluated if the left argument is false.
- ?:
- This one is tricky because internally elvis always uses binary (two operand)
operators.
In C this is a ternary operator but in elvis it is implemented as two binary
operators which cooperate in a subtle way so they seem like a single ternary
operator.
You probably don't need to know the details, but the upshot of it all is
that 1) It associates left-to-right (instead of right-to-left as in C), and
2) The
:
and third argument are optional; if omitted,
then elvis mentally sticks :""
on the end of the expression.
- ,
- (That's a comma, not an apostrophe.)
Concatenates two strings, with a comma inserted between them.
This can be handy when you're passing arguments to the
quote()
and unquote()
functions.
- ;
- Concatenates two strings without inserting any extra characters.
The result is exactly like
(no operator)
, except that
(no operator)
has an extremely high precedence, and
;
has an extremely low precedence.
:calc 1+2 3*4
93
:calc 1+2;3*4
312
13.2 Simpler Syntax
In comparison to the normal expression syntax,
the simpler syntax makes it easier to enter literal strings because
outside of parentheses the only special characters are the backslash,
dollar sign, and parentheses.
(These may be escaped by preceding them with a backslash.)
Inside parentheses, the normal syntax is used.
The :eval command uses the simpler syntax,
and the :echo command displays its arguments.
These commands can be used together to experiment with the simpler syntax,
the same way we used :calculate to experiment with
the normal syntax.
:eval echo TERM=$TERM
TERM=xterm
:eval echo home=(home)
home=/home/steve
:eval echo 2+2=(2+2)
2+2=4
13.3 Functions
There are many built-in functions.
When you call one of these functions, there must not be any
whitespace between the function name and the following parenthesis.
All of these functions accept a single string as an argument.
Omitting the argument is equivelent to passing an empty string ("").
Some functions are described as taking two arguments; actually, they
take one argument and divide it at the first comma character.
The built-in functions are:
- absolute(filename)
- Return a full pathname for filename.
- alias(name)
- Return true if an alias exists with that
name, or false otherwise.
- basename(filename)
- Return the substring of the filename without any directory names or
extension.
For example, `
basename("foo/bar.c")
' would return "bar".
- buffer(bufname)
- Return true if a buffer exists with that
name, or false otherwise.
- char(number)
- convert number to a single ASCII character, as a string.
For example, `
char(65)
' returns "A".
- current(item)
- value indicating an aspect of elvis' state.
The item can be any of the following:
Item | Returned data |
"line" |
Current line number.
|
"column" |
Current column number.
|
"word" |
The word at the cursor location.
If the cursor isn't on a word, then this returns an empty string.
(Note: To get the contents of the current line,
use the line() function.)
|
/regexp/ |
Text matching the regexp and containing the cursor.
For example, current(/\S*/) returns the current
whitespace-delimited word.
Note that / regexp/ is equivelent to
"/ regexp" .
|
"tag" |
If the showtag option is true, then
this returns the name of the tag that is defined at the cursor location, or
the nearest one before it. If the showtag option is false, or the
cursor is located above the first tag defined in this file, then current("tag")
will return an empty string.
|
"mode" |
Current key parsing mode.
This returns the same string that the
showmode option displays, except that this
function converts it to all lowercase, and strips out whitespace.
The usual return values are "command", "input", and "replace".
If the window isn't editing the its main buffer (i.e., if you're entering
an ex command line, regular expression, or filter command) then this function
will return an empty string.
|
"selection" |
Visible selection type.
This returns one of "character", "rectangle", or "line" to indicate the type
of visible selection which is currently marked in the window, or
an empty string if no visible selection is marked.
|
"next" |
Next file.
This returns the name of the file that the :next
command would load, or an empty string if you're at the end of the args list.
|
"previous" |
Previous file.
This returns the name of the file that the
:previous command would load, or
an empty string if you're at the start of the args list.
|
"tagstack" |
If the window's tag stack is empty, this returns "".
Otherwise it returns the name of the buffer to which
:pop would move the cursor. |
- dirdir(filename)
- Return the directory portion of the filename,
like dirname(filename).
- dirext(filename)
- Return the extension portion of the filename
(including the . character).
- dirfile(filename)
- Return basename and extension portions of the filename.
For example, `dirfile("/usr/tmp/filename.ext")' would return "filename.exe".
- dirname(filename)
- Return the directory portion of the filename.
- dirperm(filename)
- Return a string indicating the permissions on the file named
filename.
The possible return values are:
Output | What it means |
"invalid" |
The argument is malformed; it could not possibly be a valid file name. |
"badpath" |
The argument is a pathname, and one or more of the directories named
in that pathname either doesn't exist or is something other than a directory. |
"notfile" |
The argument is the name of something other than a file;
for example, it may be a directory. |
"new" |
There is no file, directory, or anything else with the given name. |
"unreadable" |
The file exists but you don't have permission to read it. |
"readonly" |
The file exists and you can read it, but you don't have permission to write
to it. |
"readwrite" |
The file exists and you can read or write it. |
- elvispath(filename)
- Search for a file named filename in each directory named
in the elvispath option.
Return the full pathname of the first file found, or "" if the file can't
be found in any of those directories.
- exists(filename)
- Return true if a file named
filename exists,
or false otherwise.
- feature(name)
- Return true if a this version of elvis
supports the named feature,
or false otherwise.
Currently
feature()
returns true
for all
supported display modes, network protocols, and maybe "showtag" and "lpr";
it returns false
for anything else.
As new features are added to future versions of elvis,
I expect to add them to feature()
's list.
- fileeol(filename)
- Try to guess the type of newlines used in the named file.
The list of possible return values is the same as the legal values for
the readeol option.
If filename is an http: or ftp:
URL, then this function always returns "binary" because it would take too long
to do the real check.
If filename is the name of a nonexistent or unreadable file, then
it returns "text".
Otherwise, it reads the first few hundred bytes of the file and looks for
any type of newlines, or for NUL bytes suggesting it is a "binary" file.
- getcwd()
- Return the name of the current working directory.
- hex(number)
- Convert number to hexadecimal, and
return it as string of hex digits preceeded by "0x".
- htmlsafe(string)
- Convert all characters of string from ASCII to HTML.
Most characters are unaffected by this;
the only characters that are changed are "<", ">", and "&".
- isnumber(string)
- "true" iff string is a decimal number
- Return true if string
looks like a number,
or false otherwise.
Some of the expression operators behave differently depending on whether
their arguments are numbers or strings;
this function can be used to predict which behavior the operator will employ.
- knownsyntax(filename)
- Attempt to lookup the filename extension in the
elvis.syn file.
If found, return the name of the language;
otherwise, return an empty string.
- line(bufname, num)
- Return the contents of a given line.
If invoked with no arguments or with "" as the only argument,
it returns the contents of the current line of the current buffer.
If invoked with one argument, it assumes the argument is a line number in
the current buffer, and it returns that line's contents.
If invoked with two arguments, it returns the contents of the given line of
the given buffer.
For invalid input, it fails.
- newbuffer(bufname)
- This function has the side-effect of creating a buffer.
If invoked without any arguments, or with "" as the only argument,
it creates a buffer with a name of the form "Elvis untitled #n"
where n is an integer chosen to make the name unique.
Otherwise (if invoked with a buffer name as an argument) it creates the
buffer, or deletes the contents of that buffer if it already existed.
It returns the name of the buffer.
Note that the buffer is not necessarily associated with any particular file.
In particular, this function does not load a file's contents into the buffer.
- octal(number)
- Convert number to octal, and
return it as string of octal digits preceeded by "0".
- quote(list, str)
- Return a version of str which has backslashes inserted before
any existing backslash, or before any character which appears in the
list argument.
For example...
:set t="/* regexp */"
:set r="*^$/.["
:eval /(quote(r, t))/
... will search for the next instance of of the literal string
"/* regexp */".
The '/' and '*' characters won't be treated as metacharacters in the regular
expression, because the quote() function inserts backslashes before them.
- shell(program)
- Run program, and return its output (i.e., the text that it writes to
stdout).
The final newline, if any, is removed.
If the output is too large to fit in elvis' evaluation buffer, then it will
be truncated.
Generally, it is a good idea to limit the output to a single line.
The following example will fetch the computer's name,
by running the Unix "uname" program:
:let n = shell("uname -n")
- strlen(string)
- Return the number of characters in string.
- tolower(string)
- Return a lowercase version of string.
- toupper(string)
- Return an uppercase version of string.
- unquote(list, str)
- Return a version of str,
after removing backslashes before another backslash or
any characters in list.
This is the opposite of the quote() function.
13.4 EX Commands Which Use Expressions
The :calculate command evaluates its
argument using the normal syntax, and displays the result.
The :if command evaluates its argument using
the normal syntax.
If the resulting value is any Boolean true value then a
flag is set; otherwise the flag is reset.
After that, you can use :then and
:else commands to conditionally execute some
commands, depending on the state of that flag.
The :eval command evaluates its arguments
using the simpler syntax.
The resulting string value is then interpreted as an EX command line.
This gives you a way to use the expression evaluator with commands which
otherwise wouldn't evaluate expressions.
The :let command allows you to change the
values of options.
Its syntax is ":let option=expression
",
where expression is any expression using the normal syntax.
You can use this to change the value of any unlocked option,
similarly to :set.
:set i=14
:calc i
14
:let i=i+1
:set i?
i=15
:eval set i=(i*2)
:calc i
30
:let elvispath="."
:let list="false"
:let sidescroll=0x10
13.5 VI Commands Which Use Expressions
There is only one way to use expressions in a visual command:
Move the cursor to the start of some expression in your edit buffer,
hit the lowercase v key, move to the other end, and
then hit the = key.
Elvis will then evaluate the highlighted expression, and replace the original
expression with the result.
Note that the = operator only works this way
when used with the v command for marking characters.
If you visibly mark lines, or use the traditional =movement
syntax, then elvis will send the selected lines though the external filter
program named in the equalprg option.
The # command doesn't use expressions, but
it does perform some simple math.
13.6 Other Uses of Expressions
13.6.1 Messages
All of elvis' warning and error messages are actually expressions,
using the simpler syntax.
When outputting a message, elvis may supply other parameters which are
accessible as $1 through $9.
See the Messages chapter for a longer description
of how elvis handles messages.
13.6.2 Options
The ccprg and
makeprg options' values are expressions,
using the simpler syntax.
When evaluating these expressions, $1 is replaced by
whatever arguments are supplied on the ex command line, and $2 is replaced by the
the name of the file being edited.
13.6.3 File Names
File names are evaluated as expressions (using the simpler syntax),
primarily as a means for expanding environment variable names.
This is done prior to wildcard expansion.
The full power of the expression evaluator is available; you can use
it to do more than just expand environment variable names.
For example, you could store the name of a file in one of the user
options, and then later use that option name in parentheses wherever
a filename was expected.
:set f=myfile.txt
:w (f)
wrote myfile.txt, ...
If you use this trick, remember that it only works when elvis is expecting
a file name.
It won't work when invoking external programs, because elvis doesn't know
which program arguments are supposed to be file names.
Elvis always passes program arguments literally.
Recall that when a backslash character is followed by an alphanumeric
character, both the backslash and the alphanumeric character become part
of the resulting value.
This was done mostly for the benefit of file names.
If the backslash was always dropped
then MS-DOS users would have a heck of a time entering pathnames of files!
By making the backslash a little smarter, we avoid that problem.
:eval echo c:\tmp \(notice the backslashes\)
c:\tmp (notice the backslashes)
To simplify the task of writing portable ex scripts,
the behavior of the / operator has been extended.
When one or both of its arguments are strings, it concatenates them as
a directory name and a file name, yielding a full pathname.
13.6.4 Buffer names
Ex commands allow you to specify a buffer name by putting its name in
parentheses.
You can have elvis compute a buffer name by putting '=' and an expression
inside parentheses.
For example, if option x is set to "foo"
then ":(=x)%p
" is equivelent to ":(foo)%p
".
(They both print the contents of the buffer named "foo".)
This feature was added because using :eval
to evaluate buffer
names was cumbersome.
The parentheses have special meaning in both expressions and ex addresses,
and :eval
affects parentheses and backslashes throughout the line,
not just in the buffer name.
The following example demonstrates the two techniques, deleting any backspace
sequences in the buffer whose name is stored in option x.
Clearly, the ":(=x)
" version is smaller and easier to understand.
:eval \((x)\)%s/.\\b\\\(.\\\)/\\1/g
:(=x)%s/.\b\(.\)/\1/g