Before being parsed according to the rules laid out above, Anise scripts are run through a preprocessor which looks for a certain set of instructions. As you'll see, these instructions server a variety of purposes, and generally serve to change the state of the Anise engine as it parses the script and creates objects. Preprocessor directives must appear on a line by themselves (although whitespace before and after is ignored).

The #import Directive

This directive works exactly like the #import or #include directive in many other languages. The specified file is read and placed into the script at exactly the spot where the directive appeared. The Anise engine will automatically ignore multiple imports of the same file and import cycles.

The #using Directive

This directive tells the Anise engine to use the named assembly when attempting to resolve type names. The assembly name need only be the simple assembly name (i.e., the portion of the filename before the ".dll"), and the appropriate DLL file must be in one of the standard location where the C# runtime searches for DLLs.

The #codebase Directive

This directive adds a directory to the places Anise will look for DLLs specified by the #using directive. The given path may either be absolute, or relative to the Anise script in which the directive appears.

The #namespace Directive

When using the _class meta-property, one ordinarily has to write out the fully-qualified name of each class. In order to avoid the extra typing, one can use the namespace directive with the desired namespace to set the default namespace of any future class declarations. Any _class meta-properties which occur after a namespace directive can omit the namespace portion of the type name, and just use the class name. If you would like to specify a class in a different namespace, you can override the default by providing the full namespace for that class. To revert to using the root namespace, simply provide a namespace directive with no argument.

The #environment Directive

This directive allows you to control whether environment variables should be expanded within a script. It accepts either "on" or "off" as its argument. This directive may be used multiple times within a single script to turn this feature on an off selectively as needed. By default, the feature is off.

The #build Directive

This directive will cause the named object to be instantiated as soon as the current script is finished being interpreted. This is useful to pre-load objects with container or singleton scope, or objects which are defined with a method call that produces desired side-effects. One could even use this directive to create an object which would serve as a Main method for an application:

    1: myMainObject = {
    2:     _class = MyWidgetMachine.Core.WidgetProgram;
    3:     Run = [ ];
    4: };
    6: #build myMainObject

The #converter Directive

Since Anise is very loosely typed, it is frequently the case that a value will need to be converted from the native type used within the Anise library to that required by the property or method to which it is being assigned. For this purpose, Anise includes the AniseTypeConverter base class, and a number of subclasses which handle specific conversions (e.g., from a string to an int , or from an array to a List<String> ).
All conversions happen as late as possible (e.g., just as the object is being assigned), and the results of the conversion is not saved within the state of the engine. Thus, for this Anise script:

    1: alpha = [ 1, 2, 3 ];

All of the following calls to GetObject will work as expected:

    1: int[] a = aniseEngine.GetObject<int[]>("alpha");
    2: List<int> b = aniseEngine.GetObject<List<int>>("alpha");
    3: List<string> c = aniseEngine.GetObject<List<string>>("alpha");
    4: string[] d = aniseEngine.GetObject<string[]>("alpha");
    5: HashSet<string> e = aniseEngine.GetObject<HashSet<string>>("alpha");

The converted copy of an object has prototype scope, no matter what the scope of the original object.

Another side effect of the way conversions work in Anise is that method (and constructor) calls must be unambiguous based upon name and number of arguments alone (i.e., it is not sufficient for two overloaded methods to differ only by the type of the arguments). Since Anise is so flexible about type conversions, it is often impossible to tell which method was desired.

The #converter directive allows you to register your own converters alongside those included in the Anise library. The argument to this directive is expected to be the full name of a subclass of AniseTypeConverter which implements the CanConvert and Convert methods. Consider the following example:

    1: namespace MyWidgetMachine.Core {
    2:     public class Widget {
    3:         public void ConnectFrobulator(Frobulator f) {
    4:             // Connect the formulator... whatever that means!
    5:         }
    6:     }
    7: }

    1: widget = {
    2:     _class = MyWidgetMachine.Core.Widget;
    3:     ConnectFrobulator = [ Hello world! ];
    4: };

In this case, an exception would be thrown on line 3 of the Anise script, because the Anise engine doesn't know how to convert the string, "Hello world!" into an instance of Frobulator . By implementing your own subclass of AniseTypeConverter and using the #converter directive, you can augment the Anise engine to automatically handle this type conversion:

    1: namespace MyWidgetMachine.Core {
    2:     public class Widget {
    3:         public void ConnectFrobulator(Frobulator f) {
    4:             // Connect the formulator... whatever that means!
    5:         }
    6:     }
    7: }
    9: namespace MyWidgetMachine.Converters {
    10:     public class FrobulatorConverter : AniseTypeConverter {
    11:         public override bool CanConvert(Type from, Type to) {
    12:             // Figure out if you can perform the conversion
    13:         }
    14:         public override Object CanConvert(Object value, Type from, Type to) {
    15:             // Perform the conversion
    16:         }
    17:     }
    18: }

    1: #converter MyWidgetMachine.Converters.FrobulatorConverter
    3: widget = {
    4:     _class = MyWidgetMachine.Core.Widget;
    5:     ConnectFrobulator = [ Hello world! ];
    6: };

The # (Comment) Directive

The # character is the comment character in Anise. Whenever it appears in line, the rest of that line is ignored. The comment character is treated as plain text within a quoted string, or when it is escaped with a \ character.

Last edited Jun 28, 2010 at 12:29 AM by aminer, version 1


No comments yet.