The FramScript syntax and semantics is very similar to JavaScript, JAVA, C, C++, PHP, etc. In FramScript,
- all variables are untyped and declared using "var" or "global" statements
- functions are defined with the "function" statement
- references can be used for existing objects
- no structures and no pointers can be declared
- there is the Vector object which handles dynamic arrays (integer-indexed) and the Dictionary object for associative arrays (string-indexed)
- FramScript code can access Framsticks object fields as "Object.field"
Before execution by FVM (Framsticks Virtual Machine), FramScript source code is first translated to FVM assembler source code, and then transformed to FVM machine code. Scripting is a powerful instrument and lets an advanced user control the Framsticks environment and use it according to their needs.
Statements
(brackets [] denote optional parts)
if (expression) statement; [else statement;] for ([[[var] variable=]expression];[expression];[expression]) statement for ([var] variable in expression) statement while (expression) statement; do statement; while (expression); switch(expression) { case constant: statements... default: statements... } { statement; [statement;] } return [expression]; break [level]; continue [level]; const name=value[,name2=value2[,name3=value3[,...]]]; var name[=expression][,name2[=expression2[,...]]]; global name[,name2[,name3[,...]]]; function name([parameter_names]) {statements} @include "filename" asm { source code for FVM }
Remarks:
- "level" is a positive integer specifying the loop you want to "continue" or "break". "break 1;" is equivalent to "break;", "break 2;" refers to the loop containing the innermost loop, and so on.
- in "switch" cases, case values can be variable expressions.
- for(variable in expression) iterates over some collection-type objects (Vectors, Dictionaries, Populations, GenePools, etc.).
Iteration examples:
for(var v in [1,10,100,1000]) fun(v); var dict={"key1":3.14, "key2":6.28}; for(var val in dict) fun(val); for(var k in dict.keys) fun("value for "+k+" is "+dict[k]);
Variables
Variables must be declared before they are used. Unlike in C, the declaration does not specify the variable type.
Variables are untyped, but the values are typed. The value can be of one of 5 types: integer, floating point, string, object reference or the special empty value (null).
Some operators act differently depending on the value type.
Example:
var i=123, f=1.23, s="123"; i+=0.0001; // result = 123.0001, variable type changes from int to float f+=0.0001; // result = 1.2301, variable remains a float s+=0.0001; // result = "1230.0001", variable remains a string
That's why sometimes you will need to use constructs like ""+floatvar
or int(stringvar)
in order to explicitly force type conversion. Note that the result may be of a different type depending on the order of variables (for example 2+"3"
is illegal, "2"+3
is "23"). Consequently, ""+floatvar+" "+intvar
will produce a string of two values separated by a space char. Operators: int(), float() and string() make type conversions more readable, e.g. string(2+2)
instead of ""+(2+2)
.
Global variables are declared using the "global" statement. Unlike the local ("var") variables, globals preserve their values between FramScript calls. This is useful when a function is called from outside the code module, as it happens in experiment definitions, styles, neurons and user scripts.
Example:
global counter; function eventHandler() { counter=1+counter; } function getCount() { return ""+counter; }
Named constants can be used instead of magic values to make the code more readable. Constants can be integer, float, string, or null. Vector or Dictionary constants (or any other objects) are not supported. A constant definition applies to the whole source starting from the definition point. Constants defined inside included files are visible in the source that included them.
Example:
const PARAM_READONLY=1, M_PI=3.141592, DATA_FILE="result.txt"; if (obj.getFlags(i) & PARAM_READONLY) Simulator.print(obj.getId(i)+" is readonly"); var area=M_PI*r*r; var text=File.getContents(DATA_FILE);
Expressions
Most of the C language expressions are also valid in FramScript. This includes arithmetic and logical operators, parentheses, function calls, and assignments. Moreover, the following FramScript specific operators are available:
typeof(x)
expression – determine the expression value type. It returns a string containing the class name for arguments that are objects, or one of the following simple type names: "null" "int" "float" "string" "invalid"..
(dot) – member access operator (like in C++). It can be applied to any expressions containing object references, or to the special static object names. Static object names are equal to the class names shown in the Class Navigator, e.g.:var expdefname=Simulator.expdef; // access object field Simulator.load("experiment.expt"); // invoke object method var two=Math.sqrt(4); // invoke object function
There is only one "static object" for each class. Some objects usually have a single instance and are accessed using this method (like the Simulator or Math in the example above).
Multiple objects of the same class are accessed indirectly with using object references:var g=LiveLibrary.getGroup(0); // get the reference var info="This group is called "+g.name; // use the reference
Sometimes, the static object is used to create more objects:var v=Vector.new(); // static object creates a dynamic object v.add(123); // the reference is used var s=v.size;
The special syntax "Object.Class:Member" can be used to provide the class name of the object in cases where it cannot be determined by the compiler from the object expression. If you know programming – this is similar to casting. This syntax is generally used by advanced programmers as an additional hint:var c=...some...creature...; c.movve(1,2,3); // this typo will only be detected at runtime // (the compiler does not know the class name, // so it does not try to check the member name) c.Creature:movve(1,2,3); // but this syntax results in a compilation error // (we tell the compiler that "c" is of type "Creature")
"ClassName.*" can be used as a reference to the special static object if it has to be passed to some function:var v=callNew(Vector.*); var d=callNew(Dictionary.*); function callNew(obj) {return obj.new();}
object.[expr]
– indirect member access operator. Works like the regular "dot" operator but the member name is passed as an expression:var which=2; var value=Genotype.["fit"+which]; // works like Genotype.fit2
{ "key": value, "key": value , ... }
– Dictionary creation expression. It is a short form of:
d=Dictionary.new(); d.set("key",value); v.set("key",value); ...
Keys must be string, values are any valid expressions, for example:var database = {"John":{"city":"Framsville", "favorite_numbers":[1,2,3]}, "Mary":{"city":"Nullburg", "favorite_numbers":[Math.pi, 666, 0]}};
[]
– Vector operator (Vector is a simple array-like object)
The [] operator can be used in two ways:[ expr1, expr2, ... ]
– Vector creation expression. It is a short form of:
v=Vector.new(); v.add(expr1); v.add(expr2); ...
In some programming languages a similar object is called (a dynamic) Array.
Examples:var v = ["Vector elements", 123, ["Vector","inside","a","Vector!"], "and the next one is an empty Vector:", []]; var v2 = ["Elements do not have to be constant", Math.rnd01, String.replace("hello","l","L"), v[2]];
object[index]
– indexing operator for accessing individual items of Vectors, Dictionaries and other objects implementing .get() and/or .set() methods.
Examples:var v=["one","two","three"]; var third=v[2]; // == v.get(2) v[10]="10th value"; // == v.set(10,...), this will expand the Vector to 11 elements (0 to 10) var d={"name":"value"}; var value=d["name"]; // == d.get("name") var deeper={"key1":[1,2,3], "key2":[100,200,300]}["key2"][1]; // == 200 d["name"]=12345; // == d.set("key",12345); for(var i=0;i<d.size;i++) print(d[i]); //integer keys are a way to iterate a Dictionary
var d={"size":123}; var v1=d["size"]; // ==123 - equivalent of d.get("size"), gets the value of // Dictionary element associated with the key "size" var v2=d.["size"]; // ==1 - equivalent of d.size, gets the value of the object field
object->key
– member-like arrow syntax for accessing .get()/.set() compatible objects (mostly Dictionary).
object->key
works exactly likeobject["key"]
, but when using the arrow syntax, "key" must be alphanumeric and must start with a letter. The standardobject["key"]
notation is more universal, while thearrow->key
syntax is meant for cases where Dictionary implements custom fields in an object and we want a concise notation that makes keys look like fields, without "quotes" and ["brackets"]. For example:
var d={}; // Dictionary d->a=1; // same as d["a"]=1; d->sub={}; // same as d["sub"]={}; d->sub->b=123; // same as d["sub"]["b"]=123;
function FUNCTIONNAME
– creates the function reference.call (FUNCTIONREF)([arguments,...])
– invokes the function using the function reference obtainad from the "function" operator:var calltable=[function one,function two,function three]; var selector=1; var fun=calltable[selector]; var result=call(fun)(argument1,argument2); // function two will be called
"""..."""
or'''...'''
- multiline string literals.
The expression creates the string literal containing the unmodified source text (including newlines and characters that would otherwise require quoting).
Examples:
var x="""four lines of text (includng the empty one)"""; Simulator.print(''' " "" """ %s """" """"" (delimited by apostrophes so """ can be contained inside the string)''' % "without multiline strings, we would need an enormous number of backslashes");
Functions
Functions are defined as follows:
All parameters are passed by value (copied). However, an argument (or a return value) can contain object reference, in which case it is the reference that is actually copied.
A function can return a value to the caller using the "return expression;" statement. If a bare "return;" is used, the return value is undefined.
Example:
function factorial(n) { if (n<3) return n; return factorial(n-1)*n; }
Functions can also have multiple names, which is useful when you need many functions with an identical body – for example:
function onLeftClick,onMiddleClick,onRightClick(x,y) { //do something... }
Using Framsticks classes, methods, and variables
See FramScript reference to learn about internal Framsticks classes, methods, and variables. You can also use the class browser (available both in GUI and command-line interfaces).