Copyright 2002-2015 Rick Mohr

To understand why action evaluation is an important reference topic, consider these example commands:

Phrase Start = {Left_ String.Length(Dictation.Get()) };

Copy to WordPad = {Ctrl+c} SwitchTo(WordPad) {Ctrl+v};

"Phrase Start" moves the insertion point to the beginning of a just-dictated phrase. It works by calling Dictation.Get to retrieve the dictated phrase and String.Length to count its characters. The count is then concatenated between {Left_ and } to construct a keystroke specifier, such as {Left_15} for a 15-character phrase. For this command to work, String.Length must be called before any keystrokes are sent.

"Copy to WordPad" copies text to WordPad. It works by sending the keystroke {Ctrl+c} to copy text, calling SwitchTo to bring up WordPad, and sending the keystroke {Ctrl+v} to paste the text. For this command to work, SwitchTo must be called after the keystroke {Ctrl+c} is sent.

Clearly Vocola is evaluating actions differently for these two examples.

Value functions and action functions

To explain action evaluation more formally we start with some definitions:

Essentially, the examples above work because Vocola calls value functions eagerly and action functions lazily.

Formally, Vocola identifies a value function as one that either:

Conversely, Vocola identifies an action function as one that either:

(See Function Attributes for a description of CallEagerly.)

How action sequences are evaluated

We offer both an informal and a formal description of how action sequences are evaluated.

Informal description

Actions evaluate to strings (possibly empty). Adjacent strings are concatenated.

Calls to value functions are evaluated eagerly; calls to action functions are evaluated lazily. Arguments to these native functions are evaluated before the call. Calls to user functions are simply unrolled.

To execute an action sequence, simplify the actions to atoms, combine the atoms, and then execute the atoms.

Formal description

An atom is either:

To execute an action sequence:

  1. Simplify the actions, producing a sequence of atoms (see below).
  2. Concatenate adjacent strings in the atom sequence.
  3. Execute the atoms, in order:
    1. If atom is a string, interpret it as a keystroke sequence and send the resulting keystrokes.
    2. If atom is a call to an action function, perform the call.

To simplify an action, producing a sequence of atoms:

  1. If action is a string, it is already an atom.
  2. If action is a reference (e.g. $1), simplify the referenced actions.
  3. If action is a call to a user function:
    1. Simplify the actuals, and bind them to the formals.
    2. Simplify the function's actions using those bindings.
  4. If action is a call to an action function, it is already an atom.
  5. If action is a call to a value function:
    1. Evaluate actuals (see below).
    2. Call the function.
    3. Convert the return value to a string.
  6. If action is a call to Eval:
    1. Evaluate the expression.
    2. Convert the return value to a string.
  7. If action is a call to If:
    1. Execute the predicate.
    2. Depending on result, simplify either the consequent actions or the alternate actions.

To evaluate an argument to a native function call:

  1. Simplify actions, producing a sequence of atoms.
  2. Concatenate the atoms to a single string (calling any action functions in the process).
  3. Convert the resulting string to the type of the associated formal.

(In the above discussion, the Repeat special form is treated as an action function.)