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
manual
- 1. Overview
- 2. Writing Trigger Script
- 3. Plugins
- 4. Source Code
- 5. Binaries
- 6. Licensing
- 7. Alternative Licensing
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.