TrainPlayer Programming Language v. 6.1

The vocabulary and grammar you use to write scripts is called TrainPlayer Programming Language, or TPL.  It recognizes a given list of commands and keywords -- some related to trains, such as setting speed and direction, and some standard programming constructs, like loops, ifs, and branches.  It allows you to create and substitute variables, and to separate frequently-used code into subroutines and procedures.  It is rudimentary but reasonably complete.

This document gives an overview of the components of the language.  It does not attempt to teach you programming, or even give you much more than a brief overview and a command reference.  The main documentation is built into the program, and can be found on the Reference tab of Script Central -- the same table of commands and variables as shown below, but with more detail and examples under each topic.

Further reading matter can be found on the Scripting page of the TrainPlayer web site.

TPL Overview

A script consists of a series of statements.  Some are comments -- text provided by the script author to annotate or explain -- and are skipped over when the script is executed.  Others are either executable statements, which cause actions to occur, or wait conditions, which cause the script to pause for some specified event or time period before proceeding.  The text in the script is sometimes referred to as code, and the act of creating it is called coding.

An executable statement begins with a command -- one of the words or phrases recognized by the language, such as those listed below -- followed by arguments providing data needed by the command. An argument might consist of a single string or numeric value, or an expression formed by combining multiple values using arithmetic operators. In place of a literal string or numeric value, you can use the name of a variable, and it will be replaced by the corresponding value when the statement is executed.

Statements are normally executed one after another as they appear in the script.  However, this behavior can be altered using flow commands.  A loop is a block of statements which are executed repeatedly a given number of times.  A branch is a jump to another place in the script, sometimes based on a decision made in an IF statement. 

A call is a jump to a separate external block of statements; these execute in their entirety, and then control jumps back to the statement after the call.  There are three variations on this.  A subroutine is a block of script code stored in a text file; a call to the subroutine executes the file and then returns.  A proc is a small routine stored between two tags within another script; the first time it is encountered, as its parent script executes, it is extracted and saved in memory under a speciried name; subsequently it can be called using this name.  A function is a body of code which carries out a calculation and returns a result to be used by the calling script.

Whenever a train script or junction action is running, it is connected to a train.  Commands in the script which do not specify otherwise are directed to this train.  Train control commands can be used to redirect commands from the script-owning train to another one.

Variables are created and assigned using a Set statement, or the equivalent Let.  This takes the name of a variable and an expression; the expression is evaluated and its value assigned to the variable.   If the name is new, the variable is created, otherwise the existing variable is updated. 

TPL Statements

A script consists of a series of statements, usually one per line in a text file. Lines starting with * are comments, and are not executed.  By default, comments starting with a single asterisk are echoed to the output window as the script runs; those starting with ** are not.  Blank lines are ignored.

An executable statement consists of a command or wait condition name -- one of the words in the first column of the table below -- followed by any required arguments.

Multiple statements may be concatenated on a single line, using semicolon (;) as delimiter.  As a special case, a wait statement may be followed immediately by a command on the same line.

Built-in Commands

A command is an instruction telling the program to take some action.  There are two types: commands accessible from the program menus vs. those built into the language.  The menu commands are described in the next section.  The built-in commands are in these categories:

Most commands consist of a single verb followed by one or more arguments telling what to act on or what to do.  In the table below, arguments are indicated by angle brackets -- you supply the value, not the brackets.  Optional arguments are enclosed in braces; if omitted, these take on default values.

speed 20 set speed of script owner to 20; start moving if stationary
echo "note to user!" display message in output window
throw J35 1 throw switch at junction 35 to position 1
drive "Broadway Limited" begin block of statements to be directed to another train

A complete list of commands is given below.  A better one is found in the Reference tab of Script Central.

Menu Commands

When you select any command from a menu, you see a cascading series of names starting with a top-level menu.  Write these names down one after another and you have a command you can use in a script.  Menu names must be separated by spaces, multi-word names must be quoted.  Examples:

file open bring up Open File dialog
file "open layout" bring up Layout Chooser; note quotes required around two-word command
train "add car" boxcar add car to selected train at insert position; note menu command applies to selected train, not script owner

Wait Conditions

A wait condition tells the script to wait for a certain event or until a certain time before proceeding.  There are only a few wait condition keywords, serving multiple purposes: AT a certain time on the clock, or when the first car of the train reaches a certain junction or station; AFTER an elapsed time, or when the last car passes the given location; or ON specified events.

at J35 stop when first car of train crosses junction 35, stop train, continue with script
after 0:0:02 speed 20 wait 2 seconds, then set speed to 20

Flow Control

Script statements are normally executed one after another.  Commands in the "flow control" category change this behavior, by jumping to other parts of the code based on some criterion.  The following types of flow control are available:

goto nextPart jump to statement "nextPart:"
call sitandwait 10 call subroutine "sitandwait.txt" and pass one argument

If Statement

An IF statement takes the form

if (val1 comp-op val2)
    code block 1
[else
    code block 2]
endif

where val1 and val2 are string or numeric values, variables, or expressions, and comp-op is a comparison operator, one of:

=  <  >  <>  <=  >=

The two values are compared using the given operator; if the result is TRUE, then code block 1 is executed; if FALSE, code block 1 is skipped, and control passes to the optional code block 2 or, if none, to the statement after the endif.

let ncars = $train(train1, length) create a variable and assign value from a system function
if (ncars < 10)
    echo "short train"
compare value to literal; if true, output this text
else
    echo "long train"
if comparison is false, output alternate text instead
endif  

While statement

A while statement defines a loop.  It takes a form similar to IF:

while (val1 comp-op val2)
    code block
endwhile

where the code block executes once every time the comparison expression evaluates to TRUE; if it is FALSE, the code block is skipped and control passes to the statement after the endwhile.

let i = 1 create variable and initialize
while (i < 10) compare variable to literal; if true, go to next statement
   echo Line @i 
   let i = i + 1
from here, jump back to start of loop
endwhile when comparison is false, jump to here

Use a CONTINUE statement within a loop to go to the next iteration, or a BREAK statement to jump out and terminate the loop.

Let/Set statement

Set/Let statements take the form

set var expr
let var = expr

where var is the name of a new or existing variable, and expr is an expression, one or more components joined by operators:

comp [op comp [...]]

A component is one of:

Op is an arithmetic operator.  Between two numeric components, it can be one of these four:

+  -  /  *

Between strings, only the + operator is used, and means concatenation of strings.

Examples:

let v = (2 + 6) / 12
let v2 = v + 99
set AllowYardMode 0
let s = "speed is " + $x_speed

Variables and Functions

A variable is a named value.  You can make up a name, assign it a value, then use the name in place of the value in a script statement.  This is the key to writing abstract code which can work on different input data.

To create a variable and/or assign it a value, you use a LET or SET statement.  To represent it in a script statement, you normally* precede it with "@" -- a symbol you can read as "contents of," which identifies what follows as a variable name.

 Example:

let CruisingSpeed = 55
...
at j22
   speed @CruisingSpeed

* You don't need to precede a variable name with @ when you refer to it in a LET or SET statement.  If you want to embed a variable contents into a larger string, use @ on both ends:

let s = "def"
echo The Alphabet: abc@s@ghi...

A system variable or function is a value supplied by the system.  You call for a system value by giving a function name, often followed by a list of arguments in parentheses.  An argument might be the id of an object, a keyword requesting a specific action or value, or an input value for a calculation or operation.  Sometimes you don't get a value back, you just call the function to do some work, using a CALL statement. 

System function names begin with "$".  A list of them is given below, and also in the Reference tab of Script Central.

 Examples:

echo Today's date is $DATE
echo Name of active layout is $LAYOUT(Name)
let r = $RAND(1,100)
echo A random percentage -> @r

let storedData = $READ("C:\myfile.txt")
let outputData = storedData + "additional info"
call $WRITE("C:\myfile_new.txt", @outputData)

A function family is a set of related functions with the same name, and a list of arguments which usually includes the identifier of an object and/or a keyword representing a specific value.  Several function families provide access to objects on the layout -- cars, trains, tracks, junctions, the layout itself -- while others work with general objects, like strings and files. 

Some functions are "writable," meaning their values can be changed in LET or SET statements.  Writable functions are indicated by "r/w" in the Reference listing in Script Central.

Example:

echo The load in box 44 is $CAR(x44, Loadname)
let newLoad = "Toy Tractors"
echo Changing load to @newLoad!
set $CAR(x44, Loadname) = newLoad

A local variable is one which is inaccessible outside of a certain limited region of code (or "scope").  Normally, when a variable is defined and assigned a value, it is "global" -- it can be referenced by any scripts or actions anywhere.  But if it is named in a preceding LOCAL statement, then it is known only to the script (or subroutine or proc) where it is defined.  Example:

* train script 1
let gv = "a global value"
local lv
let lv = "a local value"

* train script 2
echo global value was defined as: @gv
echo value local to script 1 is undefined here: @lv

An excellent discussion with detailed descriptions of each system function can be found on the web in TrainPlayer 6.1 - Extensions to the Trainplayer Programming Language (TPL).

Subroutines

A subroutine is a named body of code stored in a file outside a script.  The code can be executed within the script by inserting a CALL statement including the name of the file. 

To pass data into and out of the subroutine, one or more arguments may be included in the call.  Arguments are numbered from left to right in the call, and are represented within the subroutine as placeholders starting with "%".  For example, wherever the subroutine contains "%1", it will be replaced by the first argument in the call.

Here is a simple example of a subroutine which takes a single argument.  It doesn't do anything particularly useful, but the point of making it into a subroutine is that it could be made fancier if needed.

* subroutine sitandwait <secs> comment at first line of subroutine tells how to call
echo sitting for %1 secs... using passed arg in a string
after 0:0:%1 wait number of seconds given by arg 1, then return

These lines become a subroutine if stored as a text file sitandwait.txt in the TP Scripts folder.

A proc is a type of subroutine which is not defined in an external file, but in a section of another script.  Using proc and endproc statements within a script, you define a callable block of code and give it a name.

proc sitandwait defines proc and gives it a name
    echo ... content as above
    after 0:0:%1  
endproc ends definition

This becomes callable once the script containing it is run.  Including it within the master script guarantees this.

If either of the above is available, a script can then say

call sitandwait 5

to cause 5 seconds of idleness.

Display Strings

The term "display string" (DS) means a lengthy string for conveying information to the user or to a file. Each of the following commands takes a ds as argument, and all process it in exactly the same way:

Display strings:

Quotes in TPL

Our general philosophy is: quotes are not required (except in certain situations listed below), but may be used if desired. Programmers used to formal languages tend to prefer quoted strings. Thus either of these is valid:

echo this is a string to be output
echo "this is a string to be output"

In a display string, variables are substituted whether or not the string is quoted. Examples:

echo the speed of the crossing train is $train($x_train, speed)
echo "today is $date and it is already $time"

Quotes are optional around most function arguments and keywords. Thus both of these work:

echo the speed is $train(train66, speed)
echo the speed is $train("train66", "speed")

Where quotes are required:

1. Around a multi-word command, registry setting name, or filename -- that is, any of these which contains a space. In a menu, this applies only to a single level of a cascading menu. Examples:

file open "c:\my file.txt"   <== filename with space
file "revert to saved"       <== menu command
set "Default Car Length" 400 <== reg setting name

3. Around a string containing a parsable character, where the meaning of that depends on the context. In a function call, an unquoted string must not contain the character ")", because that would terminate the call. In a comma-delimited list, a string must be quoted if it contains comma. Examples:

call $write(c:\myfile.txt, a string with ) paren)   <== WRONG
call $write(c:\myfile.txt, "a string with ) paren") <== OK

4. Around a multi-line text block. This is the only way to define such a block, by enclosing it in double quotes and including carriage returns:

echo "this is a string
  with multiple lines

   and a blank line"

Note that if you want the string itself to include a quote, you must double it:

echo "string with embedded "" quote"


Table 1.  TPL Command Reference

This table shows the command vocabulary as it stood at some point prior to release.  For a more up-to-date listing, consult the Reference tab in Script Central.  Contents of that tab can be exported for printing if desired.

Flow Commands  
Goto <label> jump to statement after given label 
Autopause <secs> turn on pauses of given duration during train movements
If (<expr> <comp-op> <expr>) compare two expressions, branch based on result
Else begin alternate block after IF
Elseif  (<expr> <comp-op> <expr>) evaluate alternate after IF
Endif end IF block
While (<expr> <comp-op> <expr>) compare two expressions, execute or skip next block based on result
Endwhile end WHILE block
Break break out of WHILE loop
Continue continue to next iteration of WHILE loop
Exit terminate running script
Layout Commands  
Throw <jxn> [<pos>] throw switch to given position or next available
Rotate <ttbl> <jxn> [CCW] rotate given turntable to junction, clockwise unless CCW added
Tablestop stop turntable rotation 
Sound [loop|stop] <spath> play or stop sound in named wav file.  If LOOP omitted play once.
Set Commands  
Set <var> <expr> set variable to value; equivalent to LET <var>=<expr>
Reset <var> reset registry variable to stored value
Let <var>=<expr> set given variable to value of expression; create variable if needed
Subroutine Commands  
Call <routine> <arglist> call given subroutine or proc and pass any required arguments
Proc <routine> begin definition of procedure of given name
Endproc end procedure definition
Local <var[,var...]> declare local variables
Return return from subroutine or proc to calling script
Train Commands  
Forward set direction to forward
Reverse set direction to reverse
Speed <mph> set speed to given mph; starts train moving if stopped
Stop decelerate to a stop
Uncouple <slot> OR <car> OR <car><car>  uncouple at given position, at given car, or between given cars
Horn [<msec>] sound train horn for 3 sec or given time in millisec
Train Control Commands  
Train <train> select given train 
Load [Toggle] [Car/Train/Cut] <ids> [<loadname>] load given car(s) with given load or default
Unload [Toggle] [Car/Train/Cut] <ids> unload given car(s) 
Drive <train> transfer control temporarily to given train 
Enddrive transfer control back to original train
Start <train> [<mph>] start train script, or start train moving at 5 mph or given speed
User Interface Commands  
Echo <string> display given string in schedule window 
Input <var> [<prompt>] set user variable from input dialog, optionally using given prompt
Note [<string>] display given string in popup window; hide window if no string
Car Functions  
$CAR(label, ID) numeric car id
$CAR(label, Loaded) 1 if car is loaded, otherwise 0
$CAR(label, Loadname) name of load assigned to car, if any
$CAR(label, Label) car label
$CAR(label, AAR) car aar code
$CAR(label, RevEngine) 1 if car is marked as reverse engine, else 0
$CAR(label, Note) user-applied note on car
$CAR(label, Track) track number where car is currently located
$CAR(label, <any>) custom property; set blank to remove
$CAR(label, Class) general car class
$CAR(label, Type) name of car type within collection
$CAR(label, Position) trackno, dist to S end, 1=>headed S
$CAR(label, SortOrder) user-applied integer value
$CAR(label, Train_ID) id of owning train
$CAR(label, Car_ID) id of car
$CAR(label, Train) name of owning train
$CAR(label, Consist) one-word description of train consist
$CAR(label, ExcludeOps) X => exclude car from switchlists
$CAR(label, Load) current load or 'unloadable' or 'empty'
$CAR(label, Location) at or near track or station
Train Functions  
$TRAIN(name, ID) numeric train id
$TRAIN(name, NCars) number of cars in the train
$TRAIN(name, Car) label of car i, i between 0 and NCars-1
$TRAIN(name, Name) train name, either user-assigned or Train<id>
$TRAIN(name, Speed) current speed, same as returned by $SPEED
$TRAIN(name, Direction) F = forward, R = reverse, T = toggle
$TRAIN(name, Length) length in cars; returns same value as NCars
$TRAIN(name, Flip) invert train on track
$TRAIN(name, <any>) custom property; set blank to remove
Layout Functions  
$LAYOUT(Name) layout name
$LAYOUT(NTrains) number of trains on the layout
$LAYOUT(Train) name of train i, i between 0 and NTrains
Track Functions  
$TRACK(id, OccupiedBy) return labels of cars on track
$SCENERY(id, Show) show scenery object
$SCENERY(id, Hide) hide scenery object
$SCENERY(id, Toggle) toggle visibility of scenery object
$SCENERY(id, IsVisible) 1 if scenery object is showing, else 0
$STATION(id, OccupiedBy) return labels of cars in station
$TURNTABLE(id, OccupiedBy) return labels of cars on turntable
$TURNTABLE(id, Track) id of bridge track
$TURNTABLE(id, AlignedTo) id(s) of 1 or 2 locked external tracks
String Functions  
$STRING(string, Length) length of string in characters
$STRING(string, Contains, argument) returns 1 if string contains argument, 0 if not
$STRING(string, StartsWith, argument) returns 1 if string starts with argument
$STRING(string, EndsWith, argument) returns 1 if string ends with argument
$STRING(string, NextToken[, delims]) returns next substring, removes from string
File Functions  
$FILE(name, Open) open named text file, return id or 0 if fail
$FILE(id, Close) close file
$FILE(id, ReadAll) read contents and return as string
$FILE(id, ReadLine) read next line; return 'EOF' when done
$FILE(name, SaveVars) write all user variables to named file
$FILE(name, ReadVars) read all user variables from named file
System Functions  
$RAND(i1,i2) random integer between i1 and i2; defaults false,100 if not specified
$SUBSTR(i1,n,s) substring of s starting at i1, n chars long; if n = -1, go to end
$FINDSTR(sub,s) return zero-based position of sub within s, or -1 if not found
$SETTING(name) return named registry setting
$RRCLOCK(arg) rrclock functions: get, set t, add t
$READ(f) return contents of text file f as string
$WRITE(f,[a,]s) write string s to file f, optionally append; 0 if fail
$STRLEN(s) return length of s
$MSGBOX([type,]s) show message s with ok/cancel or yes/no buttons; return 1=ok/yes 0=no
$VIEW(op,name) get/set visibility of named window or feature; op=IsVisible,Show,Hide
$SWITCH(j) return switch pos at j (0 or 1 standard), -1 if j not a switch
$SYSTEM(cmd) execute system command in command box
System Variables  
$TIME current time of day (h:m:s in 24-hour format)
$DATE today's date (mm/dd/yyyy)
$LAYOUT layout name
$TRAIN name of selected train
$CAR name of selected car
$SPEED speed of train in MPH (KPH if metric settings in effect)
$KEY numeric code of last key hit on keyboard
$X_TRAIN name of crossing train -- train owning calling script
$X_SPEED speed of crossing train
$X_CAR label of crossing car
$DATADIR TP application data directory path
Wait Conditions  
AT <jxn> lead car of train crosses junction
AT <(t j d)> lead car of train crosses exact spot (dist d from jxn j on track t)
AT <h:m[:s]> specified time is shown on layout clock; h:m required, secs optional
AT <station> lead car of train enters named station
AFTER <jxn> last car of train crosses junction
AFTER <(t j d)> last car of train crosses exact spot
AFTER <h:m:s> specified actual time has elapsed on wall clock
AFTER <station> last car of train leaves station
ON STOP train comes to a complete stop
ON COUPLE train couples with another car
ON THROW <jxn> specified switch is thrown by any means
ON TABLESTOP turntable finishes rotating
ON KEY [<key>] user presses any key or specified key