# FixScript Classes Documentation

- Version: 0.4
- Homepage
- ChangeLog

This token processor allows you to use classes and other syntax additions.

Features:

- optional typing
- single inheritance model
- classes are separate from the underlying implementation type and thus can directly represent native handles, integers, hashes, etc.
- foreach statement for easy traversal of arrays and hash tables
- relaxed ordering of the top-level declarations
- provides optional automatic generation of to_string() methods
- provides API for other token processors for better integration (eg. to access type information)
- licensed under ZLIB license (no attribution required in binary builds)

## Table of contents

Table of contents:

- Types
- Constructors
- Base object type
- Interfaces
- The `this` variable
- Class definitions using private constants
- API

## Types

The built-in types are:

- `Dynamic` - the dynamic type is the implicit type compatible with any other type (often omitted)
- `Void` - used only for return types to enforce that the function doesn't return anything
- `Byte` - just an alias for `Integer` for documentation of the intent
- `Short` - just an alias for `Integer` for documentation of the intent
- `Integer` - 32-bit signed integer (but can also refer to 8bit and 16bit unsigned integers in arrays)
- `Float` - 32-bit float
- `Boolean` - boolean type (zero = false, non-zero = true)
- `String` - string type

You can also use array, hash and class types. Array types use brackets after the type, for example `Float[]` describes an array of floats. Hash types contains the type of the key in the brackets, for example `Integer[String]` describes a hash map with a `String` key and an `Integer` value.

## Constructors

The syntax for constructors is just a syntax sugar for static methods that create the object and return it. In case you need more flexibility when creating an object (eg. using a different underlying type) you can use static methods directly.

## Base object type

Classes can either extend an existing class type or optionally extend a special common class type named `Object`. This class allows to provide automatic implementation of `to_string` methods. You can also provide your own implementations of this method for custom string representations.

Some class types can't extend from Object, for example when they need serialization or are backed by other type than an array. In such cases you can also register a to_string method, albeit in a less efficient way. This is achieved by using the `Object::set_to_string(obj, func)` static method. It simply registers the provided function to a global (self-clearing) hash map with a weak reference key.

Another alternative is to simply put a function reference as a first entry in an array, the function must take one argument and must end with `_to_string` suffix.

You can then either call the `to_string` methods directly, or use `dump` or `to_string` (with newlines), these are automatically replaced with versions that know about class types. You can of course use the original functions by using `@dump` and `@to_string` functions.

This functionality is completely optional. You have to put `object.fix` file in the root of the scripts and extend the class from the `Object` type.

## Interfaces

Interfaces provide common type for otherwise different classes. This is achieved by creating a wrapper class that contains reference to the original class and a set of function references specific to that class so it can be called in a common way.

The original class contains some method, called `as_interface` (when the interface class is called `Interface`) that returns a new instance of this wrapper class. It can also cache it when desirable (using weak references if the same existing interface instance should be always used).

Here is an example:

    class Class1
    {
        var @field1;
        var @field2;
        var @field3;

        function as_some_interface(): SomeInterface
        {
            return SomeInterface::create(this, Class1::common_method#1);
        }

        function common_method()
        {
            log("Class1");
        }
    }

    class Class2
    {
        function as_some_interface(): SomeInterface
        {
            return SomeInterface::create(this, Class2::common_method#1);
        }

        function common_method()
        {
            log("Class2");
        }
    }

    class SomeInterface
    {
        var @data;
        var @common_method_func;

        constructor create(data, common_method_func)
        {
            this.data = data;
            this.common_method_func = common_method_func;
        }

        function common_method()
        {
            common_method_func(data);
        }
    }

## The `this` variable

The `this` variable used in instance functions and constructors is a normal variable. It can be assigned, or it can even contain a `null` value (in fact any kind of value). This is because the concept of classes is separated from the underlying implemenation type.

This can be used for various things, for example in callbacks you can pass array of multiple values instead of just the object and unpack it in the code by assigning the object instance into the `this` variable. Another example is using of weak references (again typically in callbacks).

Some methods can check explicitly for `null`s in `this`. For example comparison functions or `to_string` implementations.

A slight downside of this approach is that when a `null` is passed to an instance method, there is no check at the time of method call, it will error only when the object is accessed in the method, for some methods it can even succeed if it's not accessed at all.

## Class definitions using private constants

Sometimes you may want to define classes without actually using the classes token processor. For example if you're not using classes (or want it optional) but still provide the comfort of using them. Another example is the ability to make multiple versions of classes token processor (or other token processors) to work together.

The format is simple, to declare a class use a `@class_SomeClass` private constant with a string value (can be empty). The string can contain these attributes:

- `prefix` - function prefix (without the `_` at the end)
- `struct` - prefix for the constants (without the `_` at the end)
- `static` - specifies a list of methods that are static (the name includes a '`#`' followed by the number of parameters)
- `extend` - specifies a name of the super class

The value is delimited by '`=`', multiple attributes are separated by '`,`' and when the attribute can contain multiple values they're separated by a '`:`'. No whitespace is allowed.

You can define methods by declaring a `@method_SomeClass_method_name_1` private constant, where the last part is a number of parameters (including the implied '`this`' parameter). You can either specify static methods using the `static` attribute for the class, or just use `static` instead of `method` in the private constant name. Undefined methods are still recognized based on the prefix (all parameters and the return type are `Dynamic` in that case).

You can define global functions by using a `@global_some_func_1` private constant to define a function named `some_func` with a single parameter.

Every method uses the '`(SomeType, Integer, Float): Boolean`' format to specify the parameters. This means specifying the types of the parameters (the parameter for '`this`' is omitted for non-static methods), optionally followed by a return type. Whitespace is allowed.

This is an example how it can look:

    const @class_SomeClass = "extend=BaseClass,prefix=somecls,struct=SCLS,static=create#2:static#0";
    const @field_SomeClass_field1 = "Integer";
    const @field_SomeClass_field2 = "Boolean";
    const @static_SomeClass_create_2 = "(Integer, Boolean): SomeClass";
    const @method_SomeClass_instance_function_3 = "(Integer, Boolean)";
    const @global_some_func_2 = "(SomeClass, Integer): Boolean";

## API

The API allows other token processors to integrate with classes processing. To get access to the API, just import `classes` script and obtain the context. Then you can register various hooks. The hooks are added based on actual needs.

You can also use the API without classes, just use the prefixes `class_context_` and `class_type_` for the methods and pass the objects as a first parameter.

    const {
    	TYPE_DYNAMIC,
    	TYPE_VOID,
    	TYPE_INTEGER,
    	TYPE_FLOAT,
    	TYPE_BOOLEAN,
    	TYPE_STRING
    };

    const {
    	EXT_TYPE_CLASS,
    	EXT_TYPE_ARRAY,
    	EXT_TYPE_HASH
    };

    class ClassContext
    {
    	static function get(fname: String): ClassContext;
    	function register_function_call(name: String, get_types_func, adjust_call_func, data);
    	function register_postprocess(func, data);
    	function get_class(name: String): ClassType;
    	function get_const_type(name: String): ClassType;
    	function get_local_type(name: String): ClassType;
    	function get_variable_type(name: String): ClassType;
    }

    class ClassType
    {
    	static function create_array(base: ClassType): ClassType;
    	static function create_hash(base: ClassType, index: ClassType): ClassType;
    	function get_class_name(): String;
    	function get_parent_class(): ClassType;
    	function get_base(): ClassType;
    	function get_index(): ClassType;
    	function to_string(): String;
    	static function dump_list(list: ClassType[]);
    }

The types are divided into two enumerations. The simple types are just integers and you can directly use them (cast from/to `ClassType`). The extended types are contained in an array and the first entry is the type.

### ClassContext class

`static function get(fname: String): ClassContext`
  Obtains context for given script file name.
`function register_function_call(name: String, get_types_func, adjust_call_func, data)`
  Registers adjustment of function call to otherwise not defined function (the resolution is done as last). Multiple registrations to the same name can be done. Provide a name of the function (without parameter count, you can pass `null` to match all names) and the callbacks with these signatures:
  `function get_types(data, name: String, num_params: Integer, line: Integer): ClassType[]`
    Called when the function name is matched, provides number of the parameters. You need to return an array of types (the first index is for the return type, the rest is for the expected types of the parameters) or `null` if there is no function for given number of parameters. The returned array is modified and passed to the `adjust_call` callback.
  `function adjust_call(data, name: String, types: ClassType[], tokens, src: String, start: Integer, end: Integer): Integer`
    Called after the function call is generated with all type conversions. The types (after any conversion) are provided in an array (the previously given array is modified). You have also access to the generated tokens and the start and the ending index (exclusive). You need to return the new ending index after custom adjustments.
`function register_postprocess(func, data)`
  Registers function to be called after processing of classes. The registered functions are called in a reversed order to allow wrapping behavior of different token processors. The signature of the function is:
  `function postprocess(data, fname: String, tokens, src: String)`
    Called after processing of classes.
`function get_class(name: String): ClassType`
  Returns class type for given name. Must be called within processing.
`function get_const_type(name: String): ClassType`
  Returns the type for a constant. Returns `-1` if the constant with given name is not defined. Must be called within processing.
`function get_local_type(name: String): ClassType`
  Returns the type for a local variable. Returns `-1` if the local variable with given name is not defined. Must be called within processing.
`function get_variable_type(name: String): ClassType`
  Returns the type for a variable. Returns `-1` if the variable with given name is not defined. Must be called within processing.

### ClassType class

`static function create_array(base_type: ClassType): ClassType`
  Creates an array type with given base type.
`static function create_hash(base_type: ClassType, index_type: ClassType): ClassType`
  Creates a hash type with given base and index type.
`function get_class_name(): String`
  Returns name of class.
`function get_parent_class(): ClassType`
  Returns parent class.
`function get_base(): ClassType`
  Returns the base type of array or hash.
`function get_index(): ClassType`
  Returns the index type of hash.
`function to_string(): String`
  Returns the string representation of the type.
`static function dump_list(list: ClassType[])`
  Dumps list of types in string representation.