Stemscript

Stemscript

Stemscript is a scripting language built for creating tools inside Blockpad. It is loosely based on Javascript and spreadsheet formula language. In fact, the syntax used for Blockpad formulas all works in Stemscript, including for unit math.

Using Stemscript, you can create blocks and tools/macros to run inside Blockpad. In Blockpad, blocks are "mini-apps" that can run inside of a document and generate content like text, tables, or graphs. They can also generate outputs that can be used in calculation.

Basic syntax

Blockpad uses a javascript-y syntax, combined with spreadsheet formula syntax. Code lines must end with a semi-colon, and variables must be created using the var keyword (var a = 20;). "Lines" of code can be separated into multiple lines, the semi colon denotes the end. Use // for comments.

Basic Value Types

Numbers

Numbers are double-precision floating point numbers plus a unit. Numbers without a unit have null as a unit.

Example number literals
25
0.25
25 ft
0.25 gal
1/4
3 3/4
3 ft 6 1/2 in 
6 lbf/in^2
Strings

Strings are typical string values. Single or double quotes can be used. Use backslash to escape characters.

Example string literals
"example"
'strings'
"He said, /"Good morning/""
Boolean

Boolean values are typical logic values. There are no "truthy" values - numbers and strings have no meaning as booleans, and booleans have no meaning as numbers or strings.

Boolean literals
true
false
Arrays

Blockpad supports 2-dimensional arrays that can hold any value type as an item. 3+ dimensional arrays can be achieved by arrays in arrays. Matrix math is supported with number-only arrays. Brackets specify an array. Commas separate a row into columns and semi-colons specify the end of a row.

Example array literals
["hello", "how are you", "I'm fine", 12 in^3, true, 11111]

[1 in, 2 ft, 3 m; 4 lbf, 5 kip, 6 kN]

[[1,2,3], [4,5,6]; [7,8,9], [10, 11, 12]]
Objects

Objects in Blockpad are similar to objects in javascript, or dictionaries in Python. Objects are a set of key/value pairs, where the key could be seen as the 'name' of the value.

Keys are strings, but it isn't necessary to specify them as such in an object literal. Values are any other value type (number, string, array, object, etc).

Example object literals
{length: 0.7 ft, width: 20 mm, material: “mithril”} 

{name: 'Bilbo Baggins', species: 'Hobbit', height: 3 ft}

Variables

Create new variables using var VariableName;. Create multiple variables at once by separating them with commas. In the command line, creating a new variable with var isn't necessary.

Assign values to variables with the equals sign. You can assign value literals or the results of expressions.

Examples
var length, width, area;
length = 4 ft;
width = 1.2 m;
area = length*width;
var a = 5 in*pi;
var b = a*10 in;

Simple math operations

Math operators are about what you'd expect. What works in a spreadsheet works here. Math operations are units sensitive. For example, 3 m + 5 ft = 4.524 m, not 8.

+ Addition
- Negation and subtraction
* Multiplication
/ Division
^ Exponents

To assign units to a number literal, type the unit after the number, with or without a space. Compound units can be created using *, /, and ^.

var force = 5 lbf;
var area = 11m^2;
var cost = 99 usd/year;
var torque = 12.1kip*ft;
var pressure = 0.01 lbf/cm^2;

To convert a number to different units, use the to keyword and the new units after. In the example below, note that the first two conversions don't have an affect on the result for area. They would only matter if you want to view width or length.

var length = 5 m;
length = length to in;      //convert length to inches

var width = 7 m to in;      //enter width as meters and convert to inches

var area = length*width to ft^2;    //calculate area and convert to square feet

Boolean operations

Boolean tests are listed below. Testing equality (==) is one of the few departures from typical spreadsheet formula language, but this departure is consistent with the formula language inside Blockpad.

== Equal to
!= Not equal to
> Greater than
>= Greater than/Equal to
< Less than
<= Less than/Equal to

Functions

Built in functions

Blockpad comes with most typical spreadsheet functions built-in (Max, Min, etc), along with scripting functions (Each, For, etc).

To use these functions, the library that stores the function must be included in the script. If you're working in a file, many libraries are included automatically. Otherwise, you need to include them in the actual script. If you include the library in the script and it's already in the file, nothing bad happens, so it can be good practice to include libraries in any script.

To include a library in a script, use the following syntax at the top:

include(Library.Math);

Below is a list of common libraries to use in a script, you can find some of them library page. Also, the [scripts library] included important functions like Each() and For().

include(Library.Math);
include(Library.Scripts);
include(Library.Text);
include(Library.Logic);
include(Library.DateTime);
include(Library.Docs);
include(Library.Geometry);
include(Library.Lookup);

To call these functions use open and close parentheses, with the arguments separated by values inside, like in a spreadsheet.

Calling a built-in function example
var maxVal = Max(1, 4, 7);
var avg_1 = Average(1, 4, 7, 10);

Define a function

Like other programming languages, you can define your own functions and call them. Functions can also be considered a value type (along with numbers, arrays, etc).

Function literals

For one line functions

length => length^2          //length is a function parameter
(x, y) => x^2 + y^2         //x and y are function parameters

For multiple line functions

(cylRadius, cylHeight) => {             //cylRadius and cylHeight are function parameters
    var bottomArea = pi*cylRadius^2;
    var volume = bottomArea*cylHeight;
    return volume;
}

Name functions

To name functions, use variable assignment with a function literal.

Example named function
var rectangleArea = length => length^2;


var cylinderVolume = (cylRadius, cylHeight) => {
    var bottomArea = pi*cylRadius^2;
    var volume = bottomArea*cylHeight;
    return volume;
};

Function calls

Call a defined function just like calling a built-in function.

var volume = cylinderVolume(2.3 ft, 3.2 m);

if/then

Blockpad has if/else/else if blocks similar to javascript.

if (condition) {
    //code to be run
}
if (condition) {
    //code to be run
} else {
    //code to be run
}
if (condition1) {
    //code to be run
} else if (condition2) {
    //code to be run
} else {
    //code to be run
}

You can also use, the If() function, which works like a spreadsheet if function.

If(condition, value if true, value if false);

This can be just as powerful as if blocks by using function calls as the return values.

If(condition,
        functionToRunIfTrue(),
            functionToRunIfFalse()
);

For loops

Stemscript supports javascript style for loops.

for (statement to execute at beginning, condition to run again, statement to run after every loop) {
    //code to be looped over
}

In this example, an array of unitless numbers has units applied.

var i;
var arrayOfForces = [20, 25, 10, 12, 32];
for (i = 0; i < CountAll(arrayOfForces); i++) {
    arrayOfForces[i] = arrayOfForces[i]*1 lbf;
}

Stemscript also has a For() function, which loops over each item in an array. The For() function has two arguments: the array, and the function to be performed at each item.

This example sums the array of forces. (Note the function variable item could be named anything, but it will always represent the items in the array.

var sumForces;
var arrayOfForces = [20 lbf, 25 lbf, 10 lbf, 12 lbf, 32 lbf];
For(arrayOfForces, item => {
    sumForces = sumForces + item;
    }
);

Stemscript also has an Each() function, which performs a function on each item of the array, and returns a lazy array of the result. It takes two arguments: the array and the function to be performed.

This examples accomplishes essentially the same thing as the first for loop example. (The difference is that the original for loop changes the original array, but the Each() function uses a lazy array.)

var arrayOfForces = [20, 25, 10, 12, 32];
arrayOfForces = Each(arrayOfForces, 
                        forceMagnitude => forceMagnitude*1 lbf
                    );

Value operations and manipulations

Strings

Concatenate strings using the + operator or the Concat() function.

var word1 = 'Good';
var word2 = 'morning';

var sentence1 = word1 + ' ' + word2 + '!'                       // = 'Good morning!' 

var sentence2 = Concat(word1, ' ', word2, ', ', 'how are you?') // = 'Good morning, how are you?'

There are a number of functions to use for text manipulations, many based on spreadsheet formula language. Below are a few.

Len() Returns the number of characters in a string.
Left() Returns specified number of characters from left of string.
Right() Returns specified number of characters from right of string.
Mid() Returns characters from center of string, given start and end.
Replace() Finds substrings in a string and replaces them.
Arrays

As noted before, the Blockpad formula language is the same syntax as Stemscript. You can use the deep dive section on arrays for reference about math operations, etc. Here we discuss arrays in a programming light.

Arrays can be indexed with brackets, and an indexed item can be overwritten. The index starts at 0. Note that this doesn't work with lazy arrays, like if an array is made using the Each() function.

var array_1 = [1, 2, 3];
var item_2 = array_1[1];    // = 2 (item_2 is a copy of the indexed value)
array_1[2] = 45;            // now array_1 = [1, 2, 45]

var array_2 = Each(array_1, item => item*2);    // = [2, 4, 6]
var item_2 = array_2[1];    // this will give an error
array_2[2] = 90;            // this will give an error

Array items can be viewed/referenced using parentheses indexing. Parentheses indexes start at 1, and cannot be used to overwrite values. Unlike brackets, parentheses indexing can be used on lazy arrays.

var array_1 = [1, 2, 3];
var item_2 = array_1(2);    // = 2 (item_2 is a copy of the index value)
array_1(3) = 45;            // this will give an error

var array_2 = Each(array_1, item => item*2);    // = [2, 4, 6]
var item_2 = array_2(2)     // = 4 (item_2 is a copy of the index value)
array_2(3) = 90;            // this will give an error

Bracket and parentheses indexing also works for 2-dimensional arrays, with the form being (row, column). For a 2-dimensional array, just providing one index number returns that row as an array. You can use * to mean all rows or columns, so (*, columnIndex) will return that column as an array.

var array_2dim = [  1, 2, 3; 
                    4, 5, 6   ];

array_2dim[0,1]     // = 2
array_2dim[1]       // = [4, 5, 6]
array_2dim[*, 2]    // = [3; 6]

array_2dim(1,3)     // = 3
array_2dim(*, 3)    // = [3; 6]
Useful functions for arrays

Below are some useful functions for dealing with arrays. For() and Each() are covered in the loops section but are mentioned here as well.

CountAll() Returns the number of items in an array.
Rows() Returns the number of rows in an array.
Columns() Returns the number of columns in an array.
ConcatColumns() Combines arrays horizontally.
ConcatRows() Combines arrays vertically.
LinearSeries() Returns an array given start, end, and step.
First() Returns the first item in an array, with the option of a conditional function.
Last() Returns the last item in an array, with the option of a conditional function.
Where() Returns an array of the items in the provided array that meet the conditional function.
Each() Covered under loops.
EachRow() Same as Each(), but instead of items it uses the row as an array.
EachColumn() Same as Each(), but instead of items it uses the column as an array.
For() Covered under loops.
Objects

Object values are accessed with a period (.), followed by the key. New key/value pairs can be created using this, and values can be overwritten.

var person1 = { name:    'Bilbo Baggins', 
                species: 'Hobbit', 
                height:  3 ft        };

person1.name;             // = 'Bilbo Baggins'
person1.age = 111 year;   // the age key is added to person1, with value of 111 year
person1.age = 112 year;   // age is changed to 112 year
Useful functions for objects
Keys() Returns an array of the keys in an object.
KeysAndValues() Returns an array of the keys and values in an object.