- VAP Programming Language Introduction
- A First Program
- Language Elements
- Possible Manual Problems
VAPPad is a VAP file editor, bringing the modern IDE features to the VAP editing.
VAPPad 1.0 is ready and can be downloaded as part of VHelper 3.1 suite to increase your productivity.
No installation is required, just download the zip file, unzip and double click vappad.exe to run.
Click here to go to download page
For trial purpose, you need request an activation code for the full features.
In addition to the syntax highlighting
VAPPad shows you syntax and semantic errors immediately. No need to wait starting the VISSIM simulation. This should save you the back and forth time between VISSIM and a regular editor and is particularly useful when you have a large VISSIM network which takes long time to start.
Errors are shown as squiggles and in the error list.
Another example - Subroutine not defined
To see full list of errors VAP can squiggle, scroll down to the section before the VAP language introduction.
You can manually trigger VAP keywords and 68 internal functions with the usual shortcuts:
Automatically indent and beautify your code to improve readability.
Function Quick Info
Hover over internal functions to see a Quick Info tooltip.
Function Parameter Info
Move the caret in the function arguments to match the corresponding parameters.
Macro Drop-down List & Folding
Shows available macros and navigate to the corresponding one after selection. Collapse and expand the macros.
Press "Ctrl+]" when next to a parenthesis
() or bracket
 to navigate to its
matching delimiter, or "Shift+Ctrl+]" to select to its matching delimiter.
Highlight the same token
Mor Errors that VAPPad can Squiggle
Function arguments need to be numbers, constants or variables but not composite expressions.
Mismatched number of function parameters
Integer literal out of range
Tracevariable takes maximum 2 variables
Statement function such as
set_sgused as expression
Expression function such as
presenceused as statement
VAP Programming Language Introduction
VAP (vehicle actuated programming) is an optional add-on module of VISSIM for the simulation of programmable, phase or stage based, traffic actuated signal controls.
The latest version of VAP interpreter DLL is 2.16-16 (2016-08-15) at this time of writing.
VAP language itself is a dynamic, case insensitive, single scope (global scope), procedural programming language. It is similar to Python or Lua.
A First Program
PROGRAM New; VAP_Frequency 1; CONST /*********CONSTANTS***********/ MaxGreen2 = 50, MaxGreen4 = 80; ARRAY /***********ARRAYS************/ field[3,4] = [[0, 3, 4.6, 1.23], [1.3, 4, 0, 0.1], [1, 2, 3, 4]], tClearance = [0,3,0,3]; SUBROUTINE test1; /* IF Not init THEN Set_sg( 1 , off ); LowOccFlag := 1; init := 1; END; */ x := 1 . SUBROUTINE test2; y := 2 . /**** BEGIN MAIN SECTION ****/ GOSUB test1; GOSUB test2 .
It consists of 6 sections: Program, VAP_Frequency, Const, Array, Subroutine(s) and main sections.
VAP_Frequency, Const, Array and Subroutine(s) are optional, but the order matters. For example, if you put VAP_Frequency after the Const segment, you will get an error.
Program Header, Controller Frequency, Constants and Array must end with a semicolon. Subroutine(s) and main sections must end with a dot
If you define a segment, the contents of that segment can not be empty. the following empty subroutine will have parse error
/* empty subroutine will cause parse error */ SUBROUTINE test1 .
The first command of a VAP program (in a *.vap file) must consist of the keyword
PROGRAM, the name of the program (as a valid identifier) and a semicolon
This declaration consists of the keyword
VAP_Frequency, an integer value between 1 and 10 (the number of passes through the VAP logic per simulation second - default value is 1 if skipped) and a semicolon
The controller frequency needs to be a factor of the simulation resolution (time steps per simulation second).
User defined constants can be defined in this section.
A definition consists of identifier, an equal sign
= and constant value. If you have multiple constants, separate them by comma
,, then end with a semicolon
An equal sign must be used,
:= is considered as an assignment statement.
If you try to re-assign a value to a constant, VISSIM will report an error
CONST MaxGreen2 = 50, MaxGreen6 = MaxGreen2, /* OK, bound to a previous defined constant */ MaxGreen4 := 50; /* Error, must use =, not :=*/ MaxGreen8 := MaxGreen3; /* Error, MaxGreen3 is not defined before/ /* In the Subroutine or Main Segment */ MaxGreen2 := 100; /* Error, re-assign a constant */
Array (up to two dimensional) can be defined in the Array section.
Unlike typical C family language, array values are initialized by the same bracket
 as in the rank definition, instead of braces
ARRAY tClearance = [0,3,0,3];
A subroutine can not be empty, and it must end with a dot
., and there is no explicit return command.
Any number of subroutines may be defined (only limited by RAM space). Any predefined subroutine can be called inside a program using the
You can call another subroutine within a subroutine. Multiple Subroutine order does not matter in the sub sections, unlike functions in C language.
Unlike VAP 2.16 manual says on page 10
However, there is no form of recursion allowed. Therefore the currently active subroutine cannot call itself.
I find that a recursion is allowed and you can use
GOSUB to call the subroutine itself, as long as you provide an termination criteria not go into endless loop.
Like subroutines, it consists of multiple statements delimited by semicolon
;. The main program can not be empty and must end with a dot
. of main segment is ignored, so make sure all your subroutines are defined before the main program segment.
An identifer is a name used to identify a variable, a constant, a subroutine or a label. An identifer must start with a letter followed by zero or more letters, underscores and digits (0 to 9).
a := 1; /* OK */ a10 := 10; /* OK */ a_number := 10; /* OK */ 2b := 5; /* Error, starts with digit */ _cd := 6; /* Error, starts with underscore _ */ b# := 10; /* Error, invalid char # */ $m := 10; /* Error, starts with $ */
VAP is not case sensitive, abc and ABC points to the same variable. IF and if are considered as the same keywords.
PROGRAM VAP_Frequency CONST ARRAY SUBROUTINE IF THEN ELSE END GOSUB GOTO WAIT_AT UNTIL TRACE NOTRACE COMMAND ALL VARIABLE AND OR NOT START STOP RESET
keywords can not be used as variable, constant, subroutine and label name
VAP has 68 internal functions, those names are reserved too. For example
Cycle_second T Umlaufsekunde T Cycle_time Tc Umlaufzeit Tu Detection Det Anmeldung Anm Front_ends F_e Vorderkanten Vka Intergreen Intg Zwischenzeit Zz ...
Please note every function has an English long name, English short name, German long name and a German short name.
zz := 10; /* Error, zz is a short German name for Intergreen function */
VAP also has 11 signal display constants reserved as keywords.
||(red and amber together)|
||(off meaning ‘red’)|
||(off meaning ‘green’)|
VAP has two types of variables, regular and timer variables. All variables reset to zero when the simulation starts.
RESET to change timer variables. Once a timer variable is started, its value is increased by the length of the controller time step (reciprocal of the controller frequency) at the start of each pass through the logic.
Start (phase2Clearance); ... Stop (phase2Clearance); Reset (phase2Clearance);
Timer variable cannot be changed later by an assignment statement, and vice versa. For example, if you try to assign phase2Clearance to 4, VISSIM will report an error.
phase2Clearance := 4; /* Error, can not assign a timer variable */ a := 10; Start(a); /* Error, can not start/stop/reset a normal variable */
VAP has dynamic type system, a variable can be assigned to integer, then bound to a double later.
x := 10; x := 3.14; /* do this is fine, rebound to a double floating point number*/
If a varialbe is initialized, but never used later, there will be a warning for this.
There is no local scope in VAP, all variables and constants are at global scope, a variable defined in one Subroutine can be accessed or reassigned in another subroutine or main segment.
VAP has the following operators, start with the highest precedence to lowest
|6 - Highest||()||Parentheses|
|5||NOT||logical NOT (unary)|
|4||AND||logical AND (binary)|
|4||* , /||Multiplication, Division|
|4||% , \||Modulo, integer division|
|3||OR||logical OR (binary)|
|3||+ , -||Addition, Subtraction (binary)|
|2||=, <>, <, <=, >, >=||comparison|
All operators are left-associative. The precedence order is different from well established norms in C family, I personally feel this is against my expectation and should stick to the C common convention.
- Negative operator has the lowest precedence
a := 2 + -1; /* Error */ a := 2 - -1; /* Error */ a := 2 - (-1) /* OK, Wrap in () */ a := -3 - 2; /* What's the value? it is -5 as expected, seems evaluated as (-3)-2, this is against the precedence order, should be => -(3-2) => -1 */ b := 4 Or -1; /* Error */ b := 4 And -2; /* Error */
Orhas the same or higher precedence than arithmetic operators
if 1 + 1 = 2 And 2 - 2 = 0 then /* parse error */ end if (1 + 1 = 2) And (2 - 2 = 0) then /* use (), OK */ end if 3 > 2 and 2 > 1 then /* parse error, however technically it parse => 3>1>1 => 1>1 => False(0)*/ end; v := 5-2 Or 2-1; /* the value is 0, because OR has same precedence as +/-, => 3 Or 2 -1 => 1 - 1 => 0 */ w := (5-2) Or (2-1); /* as expected 3 Or 1 => 1 */ y := -2 and 4; /* the value is -1 as expected */ x := -2 or 4; /* the value is 1, NOT expected, seems VAP evaluated as (-2) or 4, however following precedence table strictly, unary - should have the lowest precedence, then should be evaluated as - (2 or 4) */ c := 5-2 and 2-1; /* the value is 3 as expected, and has the higher precedence => 5-1-1=>3 */ d := (5-2) and (2-1); /* the value is 1 as expected => 3 and 1 =>1 */ e := 2 or 2 + 1; /* the value is 2 as expected => 1 + 1 => 2 */ f := 2 or (2 + 1); /* the value is 1 as expected => 2 or 3 => 1 */ if -10 * 2 + 10 = -10 then /* Technically it should not parse and VAPPad flag this, however VAP evaluate this expression to true, seems treated (-10) * 2 + 10 = (-10)*/ y := 1 else y := 0; end;
it is strongly recommended to use parenthesis to denote what exactly you want
- no single line comments such as
//in C# and Java
- only multiple line comments
- matching is greedy, after starting delimiter
/*, it will continue and skip any letters including other
/*until meet end delimiter
*/, which means nested comments block are not valid
/* /* ok comments */ /* /* invalid nested comment blocks */ */
Integer and double floating point numbers. Integer must be within [-2147483639, 2147483639], which is different from typical 32 bit integer range in C# or Java [-2147483648, 2147483647].
Floating number must start with a digit and no scientific notation is allowed either.
x := 10; /* OK */ y := 3.14159; /* OK */ a := .5 /* Error */ b := 1.54334E16 /* Error */ c := 2147483647 /* Error, out of range */ big := 2147483650.2 + 10.2; /* value is 2147483660 using trace, seems rounded */
No string types
0 is false, any non zero is true. All variables reset to zero when the simulation starts
if -1 then x := 10; else x := 5; end if 0 then y := 10; else y := 5; end
The value of variable x is 10, y is 5
a := 1 and 2; b := not a;
The value of variable a is 1, b is 0
Please note there are no boolean literals, True or False, they are interpreted as undefined variables
if 10 > 1 = True then x := 1; /* this never will get executed */ end if TRUE then a := 1; else a := 0; end;
What's value for variable
a? Yes, it's 0
Simple assignment to a variable is how you create a new variable or rebind an existing one to a new value.
x := 1
Please note use
:= not equal symbol
Array value can also be on the left side of assignment statement
arr := 10
The GOSUB command calls the specified SUBROUTINE. After executing the subroutine, program execution continues with the next command following the GOSUB command.
IF-THEN-ELSE-END, the expression following the key word
IF is evaluated first. If the result is other than 0, the statements following
THEN will be executed. Otherwise, the program execution will be continued either with the statements following the optional
ELSE-clause or with the statements following
END if there is no ELSE-clause.
IF 1 > 0 THEN a := 5 ELSE a := 10 END;
The semicolon at the last statement of
THEN block and
ELSE block is optional.
There is no While or For loops in VAP, use Goto. Program execution continues at the command referenced by the jump label identifier following the jump command.
The jump label identifier must be directly followed (maximum of one blank space) by a colon
WAIT_AT-UNTIL command can be used to assemble a time loop. Firstly the expression following
UNTIL is be evaluated. If its value is zero(false), program execution is interrupted for the current controller time step and will continue in the next controller time step, repeating the evaluation of the expression following the WAIT_AT command. Otherwise, program execution continues with the next command after the label following
WAIT_AT Stage1 UNTIL t_actual = T1;
Following functions can be used as statement in VAP
Clear_front_ends Clear_rear_ends Interstage Marker_put P_interstage Record_value Reset Set_cycle_second Set_des_speed Set_prog Set_route Set_sg Set_stop_time_dist Sg_green Sg_red Start Start_at Stop
All the rest of functions like
Presence can only be used as expressions.
x := Presence (2) /* any vehicle front ends detected on detector #2 */
Functions below have to be used like a constant, no parentheses
() are used
Cycle_second/T Cycle_time/Tc Tele_count Prog_active
t_actual := Cycle_second - offset;
Trace to print out the variables and more. Please see the manual for detailed description.
Trace(All); Trace(Command); Trace(Variable(x, y)); NOTRACE;
NOTRACE to deactivates the trace mode and writes a line of “+” signs in the trace file in order to document the end of the latest trace protocol.
Possible Manual Problems
Page 10 recursion in Subroutine
no form of recursion allowed for subroutine. Therefore the currently active subroutine cannot call itself.
This is in contrast to the change log in Doc folder
Version 2.14 [VAP214.EXE, VAP214_E.EXE] (2002-12-05)
Subroutines can call each other and themselves (recursively).
I have done some tests, it does allow recursion.
- Page 13
Gosubstatement example - should not have
ENDat the end of CalcSignalState subroutine.
Endis only for
- Page 13
IF-THEN-ELSE-END, if the result is other than 0, the
THENwill be executed. It should be
THEN. Same thing to
- Page 14
if noTimer = TRUE THEN, there is no literal for
TRUEis treated as an undefined variable.
- Page 27 example for
Stage_durationfunction. This is wrong,
Wait_atshould be followed by a label, not by statement(s).
:=is used in the assignment. Though it's a good practice to separate assignment and comparison equal symbols, 95% time are spent writing statements. I am used to use equal symbol
=for assignment. Can't tell you how many times I got
:=expected errors. I would suggest VAP swap
=for constant definition and assignment.
;is optional for the last statement of the
ELSEblock; this with the requirement that no semicolon for the last statement in the subroutine or main segment, adds some unnecessary complexities in writing the parser.
- I have done several hand-written top down parsers before and have used parser generators such as ANTLR and Irony. I also did parser combinators with pattern matching in functional language such as F#, so I don't consider myself a total newbie. However I still spend time doing some unnecessary tricks to get the semicolons to work. As in real life, too many choices often make life harder. By requiring the semicolon at each statement end just like C# or Java, the grammar could be beautifully written as
'Subroutine' id ; (statement ;)+ .
- In addition, adding a block structure would be nice, like use dot
.to denote both the beginning and end of the block. This could simplify the parser more and make things such as code reformatting easier. Because block can be beautifully recursively defined as a kind of statement too.