Action Evaluation
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:
- A native function is a function from the Vocola function library or from another Vocola extension (as
opposed to a user-defined function).
- A value function is a native function whose purpose is to return a value (for
example, String.Length).
- An action function is a native function whose purpose is to affect the operating system or an
application (for example, Main.SwitchTo).
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:
- returns a value, or
- is declared as [CallEagerly(true)]
Conversely, Vocola identifies an action function as one that either:
- returns void, or
- is declared as [CallEagerly(false)]
(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:
- a string, or
- a call to an action function
To execute an action sequence:
- Simplify the actions, producing a sequence of atoms (see below).
- Concatenate adjacent strings in the atom sequence.
- Execute the atoms, in order:
- If atom is a string, interpret it as a keystroke sequence and send the resulting keystrokes.
- If atom is a call to an action function, perform the call.
To simplify an action, producing a sequence of atoms:
- If action is a string, it is already an atom.
- If action is a reference (e.g., $1), simplify the referenced actions.
- If action is a call to a user function:
- Simplify the actuals, and bind them to the formals.
- Simplify the function's actions using those bindings.
- If action is a call to an action function, it is already an atom.
- If action is a call to a value function:
- Evaluate actuals (see below).
- Call the function.
- Convert the return value to a string.
- If action is a call to Eval:
- Evaluate the expression.
- Convert the return value to a string.
- If action is a call to If:
- Execute the predicate.
- Depending on result, simplify either the consequent actions or the alternate actions.
To evaluate an argument to a native function call:
- Simplify actions, producing a sequence of atoms.
- Concatenate the atoms to a single string (calling any action functions in the process).
- 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.)