13. ARITHMETIC EXPRESSIONS

  • 13.1 Normal (C-like) Syntax
  • 13.2 Functions
  • 13.3 Subscripts
  • 13.4 Simpler Syntax
  • 13.5 EX Commands Which Use Expressions
  • 13.6 VI Commands Which Use Expressions
  • 13.7 Other Uses of 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 equivalent 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

    There is also a pseudo-option named "_" (the underscore character). Its value is the content of the current line.

    	:calc _
    	Its value is the content of the current line.

    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... unless there is an option with that name, in which case the option's value is used. (In other words, "$" is optional before option names, but mandatory before environment variable names.)

    	:calc $TERM
    	xterm
    	:calc $NOSUCHVARIABLE
    
    	:calc $autoindent
    	false
    	:calc $_
    	Its value is the content of the current line.

    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.
    [ ]
    Subscripts can be used for extracting substrings. This is very powerful, and is worthy of a longer discussion than will fit in this list. See the subscripts discussion, below.
    (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.

    Elvis' style of concatenation is smart about whitespace. If neither argument is an empty string, and the left argument doesn't end with whitespace, and the right argument doesn't start with whitespace, then Elvis will insert a single space character between them. Otherwise it concatenates them without adding or removing any characters.

    If you don't want Elvis to add whitespace, use the ; operator instead.

    ~
    (tilde) 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.)

    If the first argument to * is a string instead of a number, then the second value is used as the number of times to repeat the first argument.

    	:calc "[]" * 6
    	[][][][][][]
    

    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, or false otherwise. If both arguments look like numbers, then they will be compared as numbers; otherwise they will be compared as strings.
    &
    For numbers, this returns the bitwise AND of the arguments. For strings, it may perform set intersection. See the discussion of sets, below.
    ^
    For numbers, this returns the bitwise XOR of the arguments. For strings, it may perform set difference. See the discussion of sets, below.
    |
    For numbers, this returns the bitwise OR of the arguments. For strings, it may perform set union. See the discussion of sets, below.
    &&
    If the left argument is false (any of "", "0", "false", or false) then return it; else return the right argument. Elvis differs from C in that both arguments are always evaluated. Also, Elvis' version is more powerful since the return value isn't simply a Boolean value.
    ||
    If the left argument is true (anything but "", "0", "false", or false) then return it; else return the right argument. Elvis differs from C in that both arguments are always evaluated. right argument is only evaluated if the left argument is false. Also, Elvis' version is more powerful since the return value isn't simply a Boolean value.
    .. ...
    These two operators (dot-dot and dot-dot-dot) are identical to each other. They both return a space-delimited list of integers between the left argument and the right argument. If the right argument is less than the left argument, it returns an empty string.
    ?:
    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.
    :
    When : is used without a ?, its behavior is totally different. It concatenates its arguments, with an OS-specific path delimiter between them. On Unix systems, the delimiter is ":" but on most other systems it is ";". This is useful when constructing values for path options such as includepath or tags.
    	:let includepath = includepath:home/"include"
    ,
    (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. Note that this has a much lower precedence than (no operator).
    	:calc 1+2 3*4
    	1+2 32 32 32 3
    	:calc (1+2)(3*4)
    	3 12
    	:calc 1+2;3*4
    	312

    13.2 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 equivalent 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". (If you want "bar.c", use the dirfile() function.)
    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)
    Return a value indicating an aspect of Elvis' state. The item can be any of the following:
    ItemReturned 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.)
    "wspell" The spell-checker word at the cursor location. This differs from the normal "word" in that the spell checker allows apostrophes to appear in words if they have a letter on both sides.
    /regexp/ Text matching the regexp and containing the cursor. For example, current(/\S*/) returns the current whitespace-delimited word. Note that
    /regexp/ is equivalent to "/regexp".
    "tag" If the show option contains the keyword "tag", then this returns the name of the tag that is defined at the cursor location, or the nearest one before it. If the show option doesn't contain "tag", 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 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.
    "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.
    "face" The face name (as used in the :color command) at the cursor location. This value is only updated when the window is updated.
    "region" The current region's face name.
    "rcomment" The current region's comment.
    "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.
    "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.
    "background" If the normal background color is known, then this returns "light" or "dark" to describe it. If the background is unknown (which can only happen with the termcap user interface), it returns "".

    This function is different from the background option -- current("background") examines the RGB values of the current color, while the background option is merely something that you can explicitly set to "light" or "dark", independent of what the real background color happens to be. Note that the :color command's choices for foreground colors is affected by the background option only if current("background") returns "".

    dirdir(filename)
    Return the directory portion of the filename, like dirname().
    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.ext".
    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:
    OutputWhat 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 or directory; for example, it may be a named socket or block device.
    "directory" The argument is the name of a directory (folder).
    "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.
    color(face)
    color(face,"set")
    color(face,"fg")
    color(face,"bg")
    color(face,"like")
    If face is unknown, this returns "" regardless of any other arguments.

    Otherwise, when passed only a face name, it returns the attributes of that face as set via the :color command. When invoked with "set" as a second argument, it returns true or false to indicate whether the colors were explicitly set, or are merely the defaults. When invoked with "fg", "bg", or "like" as a second argument, it parses the face's description and returns that particular attribute.

    Note that the foreground may be a single color, or a list of colors delimited by the word "or". Also, in "x11" at least, the background may be a single color, an XPM image file name, or both for a tinted image.

    feature(name)
    Return true if this version of Elvis supports the named feature, or false otherwise. Currently feature() returns true for all supported display modes, network protocols, and certain other words; it returns false for anything else. Specifically, Elvis will return true for any of the following which are supported by Elvis' configuration:
    NameThe tested feature
    "alias":alias command
    "array"subscripts in expressions
    "autocmd":autocmd and related commands
    "backtick"use of `program` in ex command lines
    "browse":browse command
    "complete"name completion via the <Tab> key
    "equaltilde"The :let command supports "=~ excmds"
    "fold":fold and :unfold commands
    "ftp"ftp network protocol
    "g"Commands with a g prefix
    "hex"hex display mode
    "hlobject"hlobject and hllayers options
    "hlsearch"hlsearch option
    "html"html display mode
    "http"http network protocol
    "image"background images and themes (X11 and Windows)
    "incsearch"incsearch option
    "listchars"The listchars option.
    "lpr":lpr command and printing options
    "make":make and related commands
    "man"man display mode
    "mapdb"map debugger, via maptrace and maplog options.
    "mkexrc":mkexrc command
    "normal"Vim's :normal command
    "proto"add new protocols via aliases
    "ram"-f ram command-line option (mostly for MS-DOS)
    "region":region and related commands
    "showtag"The "tag" keyword in the show option
    "smartargs"smartargs option
    "spell"spell checker
    "split":split and related commands
    "stdin"Use Elvis as filter by giving "-" as file name
    "syntax"syntax display mode
    "tex"tex display mode
    "textobj"text objects for use with vi operators
    "v"The v, V, and ^V commands
    "xft"antialiased fonts (X11 only)

    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.
    folded()
    folded(number)
    folded(bufname, number)
    unfolded()
    unfolded(number)
    unfolded(bufname, number)
    Return the name of the folded or unfolded "FOLD" region containing a given line, or "" if it has never been folded. When invoked with no arguments, it checks the current line. When invoked with one argument, it checks that line number in the current buffer. Otherwise it checks the given line number within the given buffer.
    getcwd()
    Return the name of the current working directory.
    hex(number)
    Convert number to hexadecimal, and return it as string of hex digits preceded 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)
    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()
    line(number)
    line(bufname, number)
    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.
    list(string)
    Return a version of string in which any control characters have been replaced with caret-letter sequences. Note that unlike the :list command and list option, this function does not append a $ to mark the end of the line
    newbuffer()
    newbuffer(bufname)
    This function can have the side-effect of creating a buffer. If invoked with the name of an existing buffer, then it will leave that buffer unchanged and return "". If invoked with the name of a non-existing buffer, it will create that buffer and return its name. If invoked without any arguments, or with "" as the only argument, it will create a buffer with a name of the form "Elvis untitled #n" where n is an integer chosen to make the name unique.

    Note that the buffer is not necessarily associated with any 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 preceded 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 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.

    Quite often, the character list will simply be the magicchar option. This works perfectly for quoting plain text to be used in a regular expression, with the magic option set.

    rand(number)
    rand(item ...)
    This function accesses a random number generator. If passed a number, it will return a random integer between 1 and that number. Any other argument is interpreted as a comma-delimited list of values; it will randomly choose one item and return it.
    	:calc rand(6)
    	2
    	:calc rand(6)
    	5
    	:calc rand("north", "south", "east", "west")
    	north
    	:calc rand("north,south,east,west")
    	east

    (If you want to choose from a list with some other type of delimiter, you can use an array subscript of "?".)

    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")
    spell(string)
    spelltag(string)
    String is a single word. If it appears to be correctly spelled, then spell() or spelltag() returns "". Otherwise, it returns a space-delimited list of possible corrected spellings. If no corrected spellings are found, then it returns "?".

    The spelltag() function looks up words via the tags dictionary, while spell() uses both the tags and natural-language dictionaries. These only find words that have been loaded into RAM, however. This includes all tags so the spelltag() function should work well, but Elvis only loads natural-language words the first time it displays them on the screen. This will often prevent spell() from finding the corrected word. You can use the :wordfile command or spellautoload option to load all of the natural-language words into RAM, so spell() will work better.

    strlen(string)
    Return the number of characters in string.
    time()
    time(filename)
    When invoked without any arguments, or an empty string as an argument, it returns the current time. When given the name of a file, it returns the timestamp of that file; if the file doesn't exist or is invalid, then an empty string is returned. Time strings are always of the form "YYYY-MM-DDThh:mm:ss" where the timezone is understood to be local time zone. This is an ISO-8660 compliant date format. Two timestamps can be compared as strings, using Elvis' normal relational operators.
    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.
    window(buffer)
    Return the windowid of a window that is currently showing buffer, or "" if there is no such window. (See also the :window command for a way to switch windows.)

    13.3 Subscripts

    Elvis can be compiled with support for subscripts in expressions. To determine whether your version of Elvis supports this, you can check the value returned by feature("array"). If True then subscripts should work.

    13.3.1 Extracting substrings

    Subscripts offer a way to extract parts of a string. By default it divides the string into words. By placing index numbers in square brackets at the end of a string expression, you can extract the indexed words.

    	:let a="this is a test"
    	:calc a[1]
    	this	
    	:calc a[2 3 4]
    	is a test

    Note that subscripts start 1, not 0 as in C. Positive subscripts count from the left edge of the string, and negative subscripts count from the right edge. Zero returns the number of items in the string.

    	:calc a[-1]
    	test
    	:calc a[0]
    	4

    Using "?" in the subscript chooses an element randomly.

    	:calc a["?"]
    	is

    You aren't limited to a single index number. You can use a space-delimited list of elements to extract. The ".." operator is handy for this. An empty list extracts an empty string.

    	:calc a[1..3]
    	this is a
    	:calc a[2..]
    	is a test
    	:calc a[]
    
    	:calc a[4 3 2 1]
    	test a is this
    	:calc a["?????"]
    	this a test a is
    	:calc a["?."]
    	a test

    Although the subscripting works on space-delimited words by default, it can also use any single-character delimiter. Simply pass the delimiter inside the square brackets, before the index values.

    	:calc "/var/tmp/myfile"["/", 2]
    	var
    	:calc "/var/tmp/myfile"["/", -1]
    	myfile

    Notice that the text before the first instance of the delimiter (a zero-length string in the above examples) counts as item #1, so "var" is actually the second item in that string.

    You can index by individual characters. This is denoted by giving an empty string as the delimiter.

    	:calc a["",1..6]
    	This i
    	:calc a["",7..]
    	s a test

    13.3.2 Changing substrings

    The :let command also allows you to use subscripts to change the value of part of an option's value.

    	:let a[2] = "has been"
    	:calc a
    	this has been a test
    	:calc a[0]
    	5

    There are some limitations on what subscripts you can use for assignments. You can't change the length by assigning to subscript [0] for example. The replaced text must be contiguous, not scattered around the string. You also can't change an element which doesn't exist yet.

    	:let a[0] = 3
    	invalid subscript for assignment
    	:let a[2 4] = "foo"
    	invalid subscript for assignment
    	:let a[6] = "dude!"
    	invalid subscript for assignment

    Remember, this may look like you're using arrays but it is really just string processing. If you want to add a new item to a string, use a concatenation operator.

    	:let a = a "dude!"
    	:calc a
    	this has been a test dude!
    	:calc a[0]
    	6
    	:let a["", 3 4] = "at"
    	:calc a
    	that has been a test dude!

    13.3.3 Field names as subscripts

    The subscript string can be a field name instead of a list of subscript numbers. Field names can be any alphanumeric strings that start with a letter. When you do this, Elvis will look for a comma-delimited item which begins with the field name followed by a ':' character. The remainder of the item is then used as the value.

    	:let n = "first:1,last:2"
    	:calc n["first"]
    	1
    	:let n["first"] = "Steve"
    	:calc n
    	first:Steve,last:2

    For field names that are constant strings, you can also use .name notation.

    	:calc n.first
    	Steve
    	:let n.last = "Kirkendall"
    	:calc n
    	first:Steve,last:Kirkendall

    In a command of the form ":let option.name=value", if the option doesn't already contain a field with the given name, then Elvis will append it.

    It is also worth noting that the listchars option uses this format. Consequently, you can use named subscripts to access its fields.

    	:let listchars.esc=""

    13.3.4 Subscript idioms

    There are some tricks you can use to simplify the way you specify a delimiter. First, since any empty expression returns an empty string, you can leave off the "" (but retain the comma) when indexing by characters.

    	:calc a[,1..4]
    	that

    Next, many punctuation characters are used as arithmetic operators, and many arithmetic operators will concatenate strings with a copy of the operator between them, if either of their arguments isn't a number. This means that you can sometimes avoid quoting operators.

    	:calc "*-courier-medium-r-*-18-*"[-,-2]
    	18
    	:calc "Portland|Oregon|97201"[|,1]
    	Portland

    Some other operators do interesting and useful things. For example, the / operator concatenates strings using the operating system's directory delimiter, and the : operator concatenates them with the path delimiter. This means that unquoted / and : can be used to pick apart file names and directory paths in an OS-independent manner.

    	:calc filename
    	C:\tmp\filename.txt
    	:calc filename[/,-1]
    	filename.txt
    	:calc $PATH
    	C:\windows;C:\windows\command;C:\msdev\bin
    	:calc $PATH[:,1]
    	C:\windows

    Here is an incomplete table summarizing some of these idioms:

          .--------.-------------------------------------------.
          | Idiom  | What it returns                           |
          |--------|-------------------------------------------|
          | a[i]   | The i'th word                             |
          | a[,i]  | The i'th character                        |
          | a[,,i] | The i'th comma-delimited item             |
          | a[/,i] | The i'th part of a filename               |
          | a[:,i] | The i'th element in a directory path list |
          ^--------^-------------------------------------------^

    13.3.5 Sets

    If feature("array") returns True, then Elvis also supports sets. Sets are represented as comma-delimited lists of names or name:value pairs.

    This format is very similar to the way Elvis uses field names as subscripts, and that's why sets are supported only when arrays are supported. This similarity means you can test whether a given element is in a set by using an expression of the form set.fieldname, like this:

    	:let a="red:255,green:150,blue:0,allocated,private"
    	:calc a.red
    	255
    	:calc a.alpha
    
    	:calc a.allocated
    	True

    Note that elements that are present but have no value will return the value of the true option, but missing elements will simply return "" (not false, although "" is treated as a false value in Boolean contexts such as an :if command). Elements with values will return their value.

    The &, |, and ^ operators will perform set intersection, union, and difference, respectively, when passed non-numeric arguments. Specifically...

    leftset & rightset
    The return value is the intersection of the sets. The value components from rightset are kept.
    	:calc 2345 & 0xff
    	41
    	:calc "a,b,c" & "a,c,e"
    	a,c
    	:calc "foo:1,bar:true" & "foo"
    	foo
    	:calc "foo" & "foo:1,bar:true"
    	foo:1
    leftset ^ rightset
    The return value is the difference of the sets. In other words, the result consists of any elements from leftset which don't also appear in rightset. The value components from leftset are kept.
    	:calc "a,b,c" ^ "b"
    	a,c
    	:calc "a:1,b:2,c:3" ^ "b:don't care"
    	a:1,c:3
    	:set listchars?
    	listchars=trail:_,tab:>-
    	:let listchars ^= "trail"
    	:set listchars?
    	listchars=tab:>-
    leftset | rightset
    The return value is the union of the sets. For name:value pairs that appear in both arguments, the value components from rightset are kept.
    	:calc "a,b,c" | "a,c,e"
    	a,b,c,e
    	:calc "a:1,b:2,c:3" | "b:two,d:four"
    	a:1,b:two,c:3,d:four
    	:calc listchars?
    	listchars=tab:>-
    	:let listchars |= "trail:_"
    	:calc listchars?
    	listchars=tab:>-,trail:_
    Notice that set operators can be handy for manipulating the listchars option. They can also be useful if you pack multiple fields into the bb and ww options.

    If both argument sets are sorted, then the resulting set from any of the operators will also be sorted. The :theme alias uses the following code to build a sorted list of available themes. Since the set of themes starts out empty (which is inherently sorted), and we only add single elements at a time (which are also inherently sorted), this means the list of themes will be sorted.

    	local a="" i d f
    	for i (1..elvispath[:,0])
    	do {
    	  let d=elvispath[:,i]
    	  for f in (d/"themes"/"*.ex")
    	  do {
    	    if basename(f) != "*"
    	    then let a |= basename(f)
    	  }
    	}
    	if a == ""
    	then error no themes
    	else calc a

    The real magic happens in the ":let a |= basename(f)" command, which adds an item to the set, while avoiding duplicates and keeping the set sorted.

    13.4 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.5 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 :while command defines the expression that is to be tested by a later :do command. The :for command, on the other hand, can evaluate its argument immediately and use the result as a list of values to be used by :do.

    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.6 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.7 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.

    Similarly, the keywordprg option uses the simpler syntax with $1 being replaced by the name of a tag to be looked up, and $2 by the current file name.

    The tagprg option, if set, replaces $1 with the arguments of the :tag command -- generally the name of the tag to be looked up, although the user is free to type in anything.

    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 equivalent to ":(foo)%p". Both commands would print the contents of the "foo" buffer.

    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