The language grammar is fairly simple, and resembles the C language
in syntax. The language supports very few constructs, such as
Methods are commands/functions which are overloaded. That is, depending on the class of the object that is invoking the same named method, different procedure-code may be executed. For example, both "txtButton" and "slider" have the render() method, but even though it is the same name "render", there are actually different 'render'ing methods. Methods (like "attributes) are inherited according to the classing inherintance scheme as described in Chapter 3.
The commands like print(), exit(), create(), etc are all implemented as methods. Instead of building the commonly used commands into the language grammar, they actually just defined early enough in the class hierarchy as to be accessible by all subclasses that may need them.
The viola language is both compiled and interpreted. It is compiled to bytecodes rather than machine executable binary. And, the bytecode is what's interpreted. Scripts are compiled down to bytecodes the first time the interpreter is asked to execute a script. Since bytecodes are cached (in core), each object's script would be compiled only once (until the process exits or if the scripts is changed).
The interpreter can recognize that an object is tagged as "untrusted". For example, those object/scripts transported over remote sites (ie: via the WWW). In which case, the interpreter will disallow those scripts/objects from executing certain sensitive operations.
Many readers should be able to skim through this section, giving more attention to sections concerning data types and automatic type conversion.
/* Viola uses C style comment, can be extended beyond one line, and is not nestable. */
There are five built-in data types: int, float, char, string, list, (actually, other type exists, but are mostly for optimization reasons). There's currently no way to define new data types at script level.
It is not necessary to declare a variable before using a variable. The interpreter internally keeps track of the type of the data assigned to a variable, and can alter the data type of the variable as new data is assigned to the variable.
Because of automatic type conversion, the complexity usually associated with typed language is somewhat hidden from the user of the language, who can more or less treat the language as typeless.
Valid example code:
A salient point to notice that after the addition to a floting point, the variable x becomes a floting point type. See Section 4.3 for the type conversion rules.
A list item is referenced in the form listname[index], and the list item count is gotten by listname (no element index).
The current implementation of a list looks conceptually like an infinite array in which each element is indexed by an unique integer number in the list. There's no need to declare type and size of the array. Just use them:
The output would be:
List the operators. Explain what conversion process occurs when two differing types of data are operated on.
Give plenty of examples, especially for cases that cause automatic data type conversion.
Comparison operators: > >= < <=
Equality operators: == !=
The operands to the above operators can be any combination of integer, float, and string ``type''.
When the two operands are of different type, automatic conversion may be performed according to the following table:
What the table means: the rows represent the possible types of the left operand, and the columns represent that of the right operand. The first item in each cell shows the conversion procedure applied to the left operand, and the second item that on the right operand. ``nc'' means no conversion performed.
Example: the expression (3 < 3.14) becomes (int2flt(3) < 3.14).
It is not necessary to explicitly do data type conversion in order to compare two differing types, because of auto conversion. But for clearity and some times efficiency, you can also explicitly convert datatypes. In the following example, x gets assigned integer 123. Note that int() is merely a method which converts its argument into the integer type-- it is not a compile time 'cast'.
Increment and Decrement Operators: ++ --
Arithmetic operators: + - * /
Work like you'd expect for integers and floats.
String concatenation: +
But this is being depreciated, so the function
The style of the control flow constructs are after the style of the C language.
Note that the
Passing messages to objects look like procedure calls. But for calling objects, it's better to use the send method.
The order of the evaluation goes from left to right. Ie: first argument is evaluated before the second, second before third, and so on.
There is a "register" variable where the value of the latest evaluation is stored. When exiting an object with an explicitly value, use the return statement with an argument.
If no argument is used with the return statement, then whatever value is in the "register" is returned.
For example, let's define an object called ``square'', which returns the square of its first argument.
A call to the object thusly:
Gets this output:
The interpret() and the tweak() methods should be used cautiously. Interpret() takes a string argument and interprets that string as a script, and within the context of the object that is calling the interpret() method. Tweak() also interprets a script like interpret(), except that it executes the script in the context of another object (the "context object" given as an argument to the tweak() method).
For certain applications the tweak() method can pose a security problem, so all objects have a "security" attribute. The intention is to restrict the priviledges of objects deem untrusted. Such object might be the ones coming from document servers over the network.
If the "security" attribute of an object is non zero, then the object can not execute the tweak() method nor can it alter its own security status. See the chapter 12 on applets (for more on this.
These are the "sensitive" methods which the interpreter won't execute if the executing object is rated as unsecure (security ttribute is non zero): accessible(), cli(), deleteFile(), destroy(), environVar(), exit(), interpret(), modalExit(), quit(), save(), saveAs(), HTTPGet(), HTTPGetNParse(), HTTPPost(), HTTPSubmit(), HTPHotListAdd(), HTPHotListDelete(), HTPHotListGet(), HTPHotListChange(), HTPHotListLoad(), HTPHotListSave(), loadFile, loadSTG(), saveFile(), system(), tweak().
This section contains more script fragments, intended to show usage and provide a better feel for the language.
A few more things to keep in mind about variables:
Variables are not saved with attributes, so if you want to, you should move the contents of a variable into an attribute, ie: "content"... [dirty tricks]
Explain basic facts about variables: variables are created on use; variables are not saved with attributes; variable scoping is normally localized to the object.
Each object has its own variable space. That is, the variable 'x' in object A is not the same as a variable of the same name in any other object.
There is actually a way to use the
A clean way for an object to get some value from another object is to send a message asking for it. For example, an object wishing to export its width information might have a straight forward script such as this:
Error reporting in viola is pretty skimpy.... A common type of message looks like this:
which basically says "Gees, I got his message, and I don't know what to do with it."
[Note on lexical errors occuring at script compilation time...]
Comment: this intrusive reporting method to stdout should be changed in the future...
A couple of tips on writing efficient scripts: (this is feeble)
If a message can be sent as a number, send it as that (integer or float) rather than a string. This saves conversions later and the cost of the
Order the switch() cases to let the most frequent cases come first. This should soon be an non issue, but right now the bytecode compiler isn't very smart about optimizations.
In the interest of execution speed, Viola2.0 compiles script into a form close to a pseudo machine language ("pcode" or "bytecodes"). The pcode is then executed by a pcode interpreter.
In the future, additional code generators may be written to produce native machine code for specific architectures, for optimal execution speed.
Since pcodes for scripts are saved, compilation for each script occurs only once in a session. Viola does not yet, however, save pcodes along with scripts in file.
Although the program hasn't yet been rigorously profiled to determine the cost of the latency time that may be incurred by compilation, the initial impression so far seems to be that the simple compilation process is well rewarded by the gains in execution speed, especially when the script is frequently re-visited.