Variable Scope

Functions do not have access to variables outside their scope. All variables must be provided as arguments.

n = 1

function incr
  n = n + 1
    # ^ Error: variable `n` is undefined.

Loops (foreach and while), try/catch, when blocks and service blocks create their own nested scope. Variables created in this scope CANNOT be accessed from the outside:

foreach [1, 2, 3] as el
a = el # ERROR

However, access to outside variables is allowed in nested scopes:

last = 0
foreach [1, 2, 3] as el
  last = el # OK

Moreover, for if/else blocks the variable will be accessible in the parent scope if all code blocks declare the respective variable.

if weather == "sunny"
  wind_kmh = 2
  wind_kmh = 10

wind_ms = wind_kmh / 3.6 # OK

However, the following is not allowed as wind_kmh wasn't declared by all code paths:

if weather == "sunny"
  wind_kmh = 2
  storm_kmh = 10

wind_ms = wind_kmh / 3.6 # ERROR

Analogous, if the else block is missing, it isn't possible to access wind_kmh in the parent scope either as it wasn't declared by all code paths:

if weather == "sunny"
  wind_kmh = 2

wind_ms = wind_kmh / 3.6 # ERROR


Storyscript provides a few special operations. One of the them is the end operation:

if something_went_wrong
    end story

This end story operation can be used to stop a story and exit immediately.

Exception Handling

  a = dangerousService calculate
  a = 42 # fallback
  submission_service send nr:a

In Storyscript try blocks catch exceptions and pass the error to the catch block.

A finally block is always entered regardless of an exception being raised or not. This is useful for cleanup commands.

You may omit both the catch and finally.

  a = dangerousService calculate
  a = 42 # fallback

Custom exceptions can be raised with the throw keyword:

if arr.contains("red")
  throw "My Exception"

It is only allowed to throw string messages.

New variables declared in a try block will only be accessible in the parent scope if the respective variable of the same type has also been declared in its catch block. The following is not possible:

  a = dangerousService calculate

b = a # ERROR

However, variables declared in the finally block are part of the parent scope:

  a = dangerousService calculate
  b = 1

c = b # OK

Null value

Every variable in Storyscript MAY be without a value and thus have the empty value null.

revenue = asterioidMining()
if revenue != null
  reporting send :revenue

The empty value null has the following properties:

  • null can be assigned to all variables
  • all variables can be equivalence compared against null

However, null comes with many runtime restrictions:

  • all operations with null will result in a runtime error
  • Array or Map index access when either the object or key is null will result in a runtime error
  • calling built-ins or functions on a null object will result in a runtime error
  • using null as an argument to a service/function/built-in will result in a runtime error if the argument type is not any

Type conversions

Storyscript provides the to operator for type conversions which allows to convert a variable from one type to another type.

For example:

"1" to int # 1
[1,2] to string # "[1,2]"
[1,2] to List[string] # ["1","2"]

In particular, to allows convenient handling of services which return any:

any_var = AnyTypedService call
b = any_var to int + 2

However, conversions may fail at runtime:

"a" to int # ERROR

Like function calls and built-ins in Storyscript, to will result in a copy of the object and further operations on the converted object will not modify the original object.

Type checking

Storyscript allows a few implicit type conversions:

  • int types are implicitly convertible to float
  • all types are implicitly convertible to any

If a type is unknown, the Storyscript compiler will infer it to any. However, any can only be passed on and must be converted to a specific type before accessing it or performing operations on it.

The generic container types List and Map can be constructed from base types or themselves. Examples:

  • List[int]
  • List[List[int]]
  • Map[int, string]
  • Map[int, List[string]]


A type must be implicitly convertible to the assignment variable.

a = 1
a = "foo" # E0100: Can't assign `string` to `int`

Apart from the assignment operator =, Storyscript provides support for arithmetic assignment operators. Arithmetic assignment operators are: +=, -=, *=, /= and %=. These shorthands work with any data type the inherent arithmetic operator works with.

a = 4
a *= 5  # Equivalent to a = a * 5

b = "foo"
b += "bar"  # Equivalent to b = b + "bar"

Boolean operations

Boolean operators are: and, or, and not. All types need to be explicitly converted to a boolean with e.g. comparison operation (e.g. a == b) or a built-in (e.g. a.empty()).

Arithmetic operations

Arithmetic operators are: +, -, *, /, % and ^. The following operations are supported for the respective Storyscript type:

Type Operations Remarks
boolean all Arithmetic operations between two booleans are implicitly converted to int
int all
float all
regexp (none)
time +, -
string +
List +
Map (none)
none (none)
any (none)

If for the arithmetic operation <left> <op> <right>, left and right have mismatching types, the compiler will try to implicitly cast left to right and right to left. If both casts fail, the operation is disallowed. Otherwise the type system will check the operation on the up-casted type.

Comparisons operations

The following types support comparison operations:

  • boolean
  • int
  • float
  • time
  • string


2 < 3                  # OK
{"a":"b:"} < {"c": "d"} # Always disallowed

Equality operations

All real types can be checked for their equality with themselves. Only the any virtual none type (e.g. from a function without a return type) can't check for its equality.

Additionally, it is allowed to compare all types with null:

a = 1
b = a == null # false

Map keys

The following types can be used as map keys:

  • boolean
  • int
  • float
  • time
  • string
  • any


a["a"]   # OK
a[/foo/] # Always disallowed

Operator precedence

Operators with a higher precedence will be evaluated first. Storyscript has the following operator precedence (from higher precedence down to lower precedence):

  • or (Or expression)
  • and (And expression
  • <, <=, ==, !=, >, >= (Comparison expression)
  • +, - (Arithmetic expression)
  • *, /, % (Multiplicative expression)
  • as (As expression)
  • ! (Unary expression)
  • ^^ (Pow expression)
  • ., (Dot expression) [...] (Index expression)
  • Literals (1, 1.2, [1, 2, 3], {a: b}, …)
  • (...) (Nested expression)


1 + 2 * 3    # 7
(1 + 2) * 3  # 9

true and false or true # true
true or false and true # true

Application Information

Storyscript has access to application level information under the keyword app, containing the following details:

app.secrets   # map of environment variables set via the CLI (more below)
app.hostname  # the full http dns hostname where your application is located
              # e.g, ""
app.version   # the release number of the application (see "story releases list" for releases)
              # e.g, "v1"
Edit this page

What story will you write?


2019. Asyncy, Inc.