home  /   who am i  /   my work  /   soap box

trigger script


download

The Trigger Script source and binaries can be downloaded below. To install the binaries, simply extract the archive to a path with read and execute permissions. To install the source, extract the source tree to a path with read/write permissions. Instructions on building the source tree can be found in the manual, here.

Description Version Last change Platform Download
Binaries (IDE and command line tool) v1.0 October 23, 2011 Win32 Tarball / Zip
Source code v1.0 October 23, 2011 Tarball / Zip / GitHub

media

TS IDE executing a script
Trigger Script IDE
TS IDE reference window
Trigger Script reference window
TSI command line executing a script
Trigger Script command line
Resolving compilation errors
Resolving compilation errors
Trading stocks and futures with Trigger Script by using a proprietary plugin
A simple tutorial on using Trigger Script to create and control a graph

manual


1. Overview

Trigger Script (TS) enables novice programmers to develop event driven algorithms. TS interfaces with plugins which provide custom functionality.

The TS parser, interpreter, IDE, and command line interface are all written in C. The source conforms to the C90 standard. C90 was chosen to provide support for Microsoft's C compiler. TS Plugins can be written with any language supporting the C calling convention. The IDE, and certain portions of the TS core library require Windows, while the parser, interpreter, and command line interface are intended to be portable.


2. Writing Trigger Script

2.1. Comments

Comments in TS begin with a '#' character, and continue to the end of the line. Below is some code making use of the comment character.

# The below variable can be set as desired
my_var = 5 # declare an integer variable, initialize it to 5

The parser ignores comments completely.


2.2. Variables

Variables can be named using any combination of letters, numbers, and the '_' character. To declare a variable, simply assign a variable to a value. For example:

my_var = 5

The above line of code creates a variable named 'my_var', with a value of 5. The type of this variable is automatically determined during compilation. In the above example, the type will be an integer. Similarly, a variable can also be a real number:

my_real_var = 3.14159

Or a variable can be a string:

my_string_variable = "Hello World"

Or, lastly, a variable may be a boolean:

my_boolean_variable = true

With TS, it is typically unimportant to know what the type of a variable is. Once a variable is assigned a value, it can be referenced in expressions and assigned to other variables. For example:

my_var = 5
my_other_var = my_var + 1

Variables follow normal scoping rules. A variable declared in a block, will only be available in that block and any nested blocks.


2.3. Mathematical Expressions

Mathematical expressions can be formed using the following operators:

x + y Adds x and y together
x - y Subtracts y from x
x * y Multiplies x and y together
x / y Divides x by y
x % y The remainder of x divided by y
x ^ y Takes x to the power of y

Standard operator precedence is respected. Expressions can be made up of constants, variables, or functions. For example:

my_var = 5
another_var = my_var + 2 * 3 - my_function()

Expressions which contain a string may only use the '+' operator, as no other operator makes sense in the context of strings. In the context of strings, '+' has the effect of concatonation.


2.4. Comparison Expressions

Comparisons can be made between values of variables, constants, or the output of functions. The result of a comparison is a boolean value indicating the validity of the comparison expression. Comparisons are made using the following operators:

x > y  Tests if x is greater than y
x >= y Tests if x is greater than or equal to y
x < y  Tests if x is less than y
x <= y Tests if x is less than or equal to y
x == y Tests if x equals y
x ~= y Tests if x is not equal to y

Comparison operators can be chained together to form more complicated comparisons. For example, the following are valid expressions:

# result is true
result = 5 > 4 > 3 > 2 > 1

# result is true
result = 5 == 5 ~= 4 > 3 < 5

2.5. Logical Expressions

Logical expressions test the truth of multiple expressions using basic logical operators. If an integer or real number is used as an operand of a logical expression, a 0 is treated as false while every other value is treated as true. If a string is used as an operand, an empty string is treated as false while a non-empty string is treated as true. Logical expressions are made up using the following operators:

x and y Test if x and y are true
x or y  Test if x or y are true
not x   Test if x is not true

In a logical expression, 'not' takes the highest precedence, followed by 'and', with 'or' have the least precedence. Logical expression operators can be chained together to form more complicated logical expressions. For example, the following is a valid expressions:

# result is true
result = 5 > 0 and 4 > 1 and 3 < 9 and 2 < 4

2.6. If Statements

If statements direct the flow of execution, so sections of code can be conditionally executed. Below is a basic if statement:

if 5 == 5
    print("Hello World")
end

In the above example, 5 == 5 is true, so "Hello World" will be printed. If statements can be composed of a series of 'elseif' keywords, or, a final 'else' keyword. The below example should be self-explanatory:

# In this example, 'D' will be printed
if 5 > 5
    print("A")
elseif 5 > 8
    print("B")
elseif 9 > 8
    print("C")
else
    print("D")
end

2.7. Loops

Several types of loops are supported by Trigger Script. Starting with the most basic, an infinite loop can be seen below:

x = 0
loop
    print("The loop has executed " + x + "times.")

    x = x+1
end

The above loop will never end, and will print the message to infinity. Two flow control statements are available to control the flow of an executing loop: 'break' and 'continue'. If break is used, the loop will end. If continue is used, the loop will restart at the top of it. For example:

x = 0

loop
    if x > 2
        break
    end

    print("The loop has executed " + x + "times.")

    x = x+1
end

print("Done")

In the example above, the message will be printed only twice until the message "Done" is printed. Similarly:

x = 0

loop
    if x < 100
        continue
    end

    print("The loop has executed " + x + "times.")

    x = x+1
end

The above example will loop infinitely as well. However, the first 100 messages will not be printed (the first message to be printed will be "The loop has executed 100 times.").

Since infinite loops are only sometimes useful, 'while' loops are also available. A while loop will execute until its associated condition is false. For example:

x = 0

while x < 5
    print("Hello")

    x = x+1
end

The above example will print "Hello" 5 times, with x being incremented after each iteration of the loop. After 5 times, x < 5 is no longer true, and the loop exits.

Notice in the while loop example that x has to be incremented manually. This can be cumbersome, so the concept of a 'for loop' also exists in Trigger Script. A for loop is constructed in the following examples:

for x = 0 to 5
    print("Hello")
end

The above case prints "Hello" 5 times. After each iteration of the loop, x is incremented by 1. Similarly, x can be decremented by the for loop instead:

for x = 5 downto 0
    print("Hello")
end

The above case prints "Hello" 5 times as well, except x starts at 5 and counts backwards to 0.

Finally, the variable does not need to be assigned in a for loop. If the variable already exists, something like this can be done:

x = 10

for x to 15
    print("Hello")
end

In this case, x starts at 10, and increments up to 15. This results in "Hello" being printed 5 times.


2.8. Actions

Actions are a key feature in Trigger Script. Actions allow specified blocks of code to only be executed when certain conditions have changed.

Actions are always at the bottom of the program. For example:

x = 5

print("message")

# Action is at the bottom
action timer(10)
    print("timed message")
end

A script can have multiple action handlers, and each action handler can have multiple triggers associated with it. For example:

pipe("in.txt")

action timer(5000), listen_pipe()
    message("Either it's been 5 seconds, or data is on the pipe")
end

The above action will be executed every time a new line is appended to 'in.txt', and, when 5 seconds have passed. The behavior of a trigger associated with an action is dependent on the trigger. Consulting the documentation for the trigger is recommended. Below are some examples of actions which will be run for various timer triggers:

# This action is run once, after 5 seconds
action timer(5000)
    print("Hi")
            end

# This action is run every 5 seconds
action ptimer(5000)
    print("Hi")
end

The arguments passed into a trigger may also be changed whenver that action is run, however, the behavior of doing so is dependent upon the trigger being used and the documentation should be consulted. For example:

x = 5000

action ptimer(x)
    x = 20
end

Even though x is changed to 20 in the above example, the timer will continue to fire every 5 seconds. However, the below example will update its timer value to 20 instead of 5000:

x = 5000

action vtimer(x)
    x = 20
end

The triggers of an action will inform the Trigger Script when they are finished (meaning they will no longer fire). A script continues to execute until all triggers are done firing. However, a script can be completed prematurely by using the 'finish' statment. For instance:

x = 0

action ptimer(5000)
    x = x+1

    if x > 5
        finish
    end
end

The above script will complete once the action has been triggered more than 5 times.


2.9. Functions

Trigger Script supports defining functions. Functions are defined by simply creating a new file. For instance, imagine two files exist:

File 1, named: foo.ts

print("this is foo.ts")

File 2, named: main.ts

foo()

In the above example, when file 2 ('main.ts') is run, it invokes a function by the name of 'foo'. This function is identified by its file name, and will be called by the script. The result of running 'main.ts' will be the printed message "this is foo.ts".

Functions can define inputs and outputs. Each function can have multiple inputs, but only a single output. Outputs must be assigned a value, and both inputs and outputs are always declared at the top of the file. For example:

File 1, named: foo.ts

input x, y, z
output my_out = 0

my_out = x + y + z

File 2, named: main.ts

result = foo(1, 2, 3)

print(result)

In the above example, a function is defined by creating a new file named 'foo.ts'. This function takes 3 arguments: x, y, and z. An output is defined, and is initialized to 0. When the function runs, the output variable 'my_out' is assigned the sum of the inputs. The function 'foo' returns when the end of the file is reached. In the above example, the value 6 will be printed.

If a function has action handlers defined, the function will always return before any action handlers are run. The action handlers will continue to run, despite the function having returned. For example:

File 1, named: foo.ts

output var = 0

var = uniform_random()

action ptimer(5000)
    print("I'm still running")

    var = 10
end

File 2, named: main.ts

result = foo()
print(result)

In the above example, foo() will return a uniform random variable, however, the message "I'm still running" will continue to print every 5 seconds even though the function has returned the result to its caller. If foo() were called multiple times, each invocation would receive it's own set of action handlers each firing every 5 seconds.


3. Plugins

3.1. Basics

Plugins provide an easy way to extend Trigger Script. With a plugin, new functions and action triggers can be written in any language supporting the C calling convention. When either the TS IDE or TSI command line tool starts, plugins are loaded from the program directory as well as the user's "My Trigger Scripts\Plugins" directory. Directories listed in the 'ts_plugin' environment variable will also be searched for plugins. The 'ts_plugin' environment variable has the form:

ts_plugin = [path1];[path2];...;[pathn]

Plugins are determined by their extension. Any DLL with the extension [name].ts.dll, will be loaded by TS.

When a plugin is loaded, the IDE will automatically build a list of functions contained in it as well as documentation for those functions. Opening the reference window in the TS IDE will make the documentation for the plugin visible.


3.2. Configuring

Plugins can be configured by the user at startup. By settings the 'ts_variable' environment variable, settings can be communicated to a plugin. 'ts_variable' takes the following form:

ts_variable = [var1]=[value1];...;[varn]=[valuen]

Each variable and value pair will be communicated to each loaded plugin. It is up to each plugin to decide what to do with the specified variables.


3.3. Writing

The types and definitions required for implementing a TS plugin are located in the source at ts/api/tsffi/include. The header files contained in this include path define everything needed to communicate with the Trigger Script language.

Any TS plugin must start by exporting a registration function. The registration function is called by TS at startup to retrieve a list of functions provided by the plugin. The registration function takes the following form:

void RegisterTSFFI (struct tsffi_registration_group** groups, unsigned int* group_count);

The function must be exported under the name "RegisterTSFFI", or the plugin will not be loaded. Upon returning, the plugin will have pointed the 'groups' variable to an array of 'tsffi_registration_group' structs and will set the 'group_count' variable to the number of entries in the array. The registration function is not allowed to fail. A group entry has the following type:

struct tsffi_registration_group
{
    unsigned int function_count;
    struct tsffi_function_definition* functions;

    tsffi_begin_module begin_function;
    tsffi_module_state state_function;
    tsffi_end_module   end_function;

    void* user_data;
};

The group specifies an array of functions definitions and a count specifying the number of entries in that array. Optionally, a begin, end, and state function can be specified. When a TS begins execution, the begin function is called. When a TS ends execution, the end function is called. When the module changes state, the state function is called. In all of these cases, the specified 'user_data' will be passed to each of the callback functions. A modules state may be either 'TSFFI_MODULE_SLEEPING' or 'TSFFI_MODULE_RUNNING'. A module is considered to be sleeping if no actions are triggered and all there is to do is wait.

Function definitions are defined through the following type:

struct tsffi_function_definition
{
    char* name;
    char* documentation;

    tsffi_function          function;
    tsffi_action_controller action_controller;
    unsigned int            output_type;
    unsigned int            argument_count;
    unsigned int            argument_types[TSFFI_MAX_INPUT_ARGUMENTS];
};

The 'name' specifies the name exported to the Trigger Script. The 'documentation' variable is optional, and specifies a string to be used as documentation the function. 'function' and 'action_controller' point to the actual function to be called by TS. Either, or both, may be set (a function may be both a normal function and an actionable function). The 'output_type' and 'argument_type' variables specify the primitive types for the function. The types can be: TSFFI_PRIMITIVE_TYPE_VOID, TSFFI_PRIMITIVE_TYPE_BOOL, TSFFI_PRIMITIVE_TYPE_INT, TSFFI_PRIMITIVE_TYPE_REAL, or TSFFI_PRIMITIVE_TYPE_STRING.

Once the registration function completes, all the functionality of the plugin will be available to any Trigger Script executed. The plugin may also specify a configuration function which takes the following form:

void ConfigureTSFFI (char* name, char* value);

This function will be called for each variable specified in the 'ts_variable' environment variable. The function is not allowed to fail, and its behavior is defined by the plugin.

This is a relatively brief overview of the plugin system. A real implementation of a TS plugin can be found in the source code for tscore.ts.dll. Specifically, tscore/ffilib/source/main.c defines a registration and configuration function, and tscore/ffilib/source/groups.c defines the list of functions provided by the plugin.


4. Source Code

4.1. FS Layout

Below is a brief description of the TS source tree:

|---+ ts
    |---+ api
        |---- tsdef
        |---- tsint
        |---- tsutil
        |---- tsffi
    |---+ application
        |---- tsi
        |---- tside
|---- tscore
|---- scintilla
Path Description
ts Defines integral components of Trigger Script.
ts/api Defines APIs used for parsing and execution of scripts, as well as the necessary types and definitions for implementing TS plugins.
ts/api/tsdef APIs responsible for parsing scripts and building a tree representation of the source code. 'tsdef' stands for Trigger Script definition. The grammar definition lives here.
ts/api/tsint APIs responsible for executing a tree constructed via tsdef. 'tsint' stands for Trigger Script interpreter.
ts/api/tsutil Wraps the APIs in tsdef and tsint to make invoking compilation and execution functionality easier.
ts/api/tsffi Defines types and definitions required to implement a TS plugin.
ts/application Applications which make use of the APIs.
ts/application/tsi The Trigger Script Interpreter command line tool.
ts/application/tside The Trigger Script IDE.
tscore The Trigger Script core plugin.
scintilla A modified version of scintilla, for use by TS IDE.

4.2. Building

Trigger Script is built using GNU Make 3.81 or higher. GNU Make for Windows can found here: http://gnuwin32.sourceforge.net/packages/make.htm

The parser and lexer for Trigger Script are generated using GNU Bison 2.4.1 and GNU Flex 2.5.4a. Bison can be found here: http://gnuwin32.sourceforge.net/packages/bison.htm and Flex can be found here: http://gnuwin32.sourceforge.net/packages/flex.htm

NOTE: Some GNU tools for Windows are sensitive to whitespaces in the installation path. To avoid any potential problems, it's recommended you install GNU Make, Bison, and Flex to a path such as 'C:\GnuWin32\' instead of 'C:\Program Files\GnuWin32\'.

Trigger Script currently targets the Microsoft Visual C Compiler. To build, launch a Visual Studio command prompt, or any command prompt which has the MSVC toolchain available. Verify GNU Make, Bison, and Flex are all available in your path.

To start the build, CD to the path where the Trigger Script source is installed. From that path, run the following command:

Prompt> make

Make will build everything, and place the output in the folder named 'build', located at the root of the Trigger Script source location. By default, a release build is performed. If a debug build is desired, run the command:

Prompt> make config=debug

The output for debug builds is also directed to the build folder.

Cleaning a build can be done specifying the target 'clean'. Clean will delete all files previously built for the specified configuration. For example:

Prompt> make config=debug clean
Prompt> make clean

5. Binaries

Trigger Script is currently composed of two different binaries: tsi.exe and tside.exe

TSI, the Trigger Script Interpreter, provides a command line tool to debug and execute scripts. For help using the command line, run:

Prompt> tsi --help

TSIDE, the Trigger Script IDE, provides a GUI interface to debug and execute scripts graphically. For help using the IDE, launch the application and press 'F1'.


6. Licensing

Trigger Script is free software. Andrew Gottemoller licenses the software on this page to you under the terms of the GNU General Public License as published by the Free Software Foundation; you can redistribute it and/or modify it under the terms of the GNU General Public License either version 3 of the license, or (at your option) any later version.

There is NO WARRANTY for Trigger Script, express or implied, including the implied warranties of MERCHANTIABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU Public License along with Trigger Script. If not, see <http://gnu.org/licenses/>.


7. Alternative Licensing

A special license for Trigger Script is available if you are unable to meet the conditions of the license described above. For more information, please contact me.