-
Notifications
You must be signed in to change notification settings - Fork 4
Language Reference
Stepper attempts to mimic constructs from popular programming languages. The structure of a well formed Stepper program is
<Annotation+>
stepper <program-name> {
statement+
}
where Annotation
is
@[ID] ( scalar )
Annotations allowed at the top level are:
-
@Comment("comment")
: Human readable description of the step function. -
@TimeoutSeconds(3600)
: Timeout for the step function in seconds -
@Version("1.0")
: Amazon States Language (currently version 1.0)
program-name
should be a valid ASL state name. State names not explicitly defined are derived from this. This is
also used as the name of the step-function when the Stepper compiler is used to create the step-function in AWS.
Here is a minimal Stepper program showing the use of annotations.
@Comment("This is a minimal stepper program")
@TimeoutSeconds(1800)
@Version("1.0")
stepper Minimal {
a = "Hello World";
}
ASL doesn't work directly with variables. Instead, ASL requires you to manipulate json attributes using InputPath
,
OutputPath
and ResultPath
. This makes for a non-standard programming model. Stepper allows you to use
variables just as you would in a conventional programming language. Behind the scenes, Stepper uses the ASL path constructs
to bring about the desired outcome. This does mean that all Stepper variables are held active in the State and therefore
must comply with the 32,768 data size limit.
Stepper variables should follow Javascript variable naming conventions (camel case). To declare a variable, simply reference it and assign a value to it using an expression or a task output. Assignment expressions should end with a semicolon.
stepper assignment {
a = 5.2;
b = 10;
c = "Hello World";
d.e.f = true;
e.f.g = false;
}
In the above code, variable a has a value of 5.2 (float), b has a value of 10 (integer), c is a string,
d is a javascript object that contains another javascript object e that has a property f with value true
. Similarly,
e has object f with property g set to false
.
You can have expressions in your assignment. Any valid Javascript expression is a valid Stepper expression with a few limitations noted below.
stepper Expressions {
a = 1; b = 2; c = 3;
d = "Hello world";
value1 = a * b + c - d.length;
value1 += a * b ;
a -= a * b -c;
c /= a * b +;
}
Stepper supports assignment and compound assignment (+=
, -=
, *=
and /=
). Calls to built-in
Javascript functions and properties are allowed. However Stepper does not support pre/post increment operators (a++, ++a)
and calls to functions that produce side-effects (e.g., total = students.push("joe");
- the push method mutates
students and returns the new length). Indexed assignments (a[i] = 3
) is also not supported due to underlying
limitations of ASL.
Talk about variables, naming convention, assignment of values and expressions supported.
Branching in Step Functions is not intuitive. One has to use the "Choice" state type and branch out to various other steps based on predicates reduced to cumbersome JSON constructs. Stepper allow you to use multiple branching constructs as you would in a regular programming language.
if (x > d.length + 10) {
// logic
} else {
// logicd
}
The if condition can be followed by a single statement or statement block. Any Javascript compliant expression can be used in the condition. However, methods that mutate state and unary operators (increment/decrement) are not allowed. You can also chain multiple if statements with if, else if constructs.
The when
construct in Stepper is like the switch construct of C/Java. However, unlike C/Java, each case statement is actually
an expression. The general syntax of when
is
when {
case <expression> : <statement-block>,
case <expression> : <statement-block>,
...
else statement-block
}
Again, expression have Javascript syntax with the same restrictions as if/else predicates. The else
block is optional and
is equivalent to the default
block of switch statements.
Stepper also supports the goto
statement to cause execution to jump around arbitrarily. As with any programming language
that supports goto
, the usual warnings apply. The syntax for goto
is
goto "<label-name>"
Labels are associated with statements and can be inserted by adding a @Label annotation before a given statement.
stepper assignment {
a = 5.2;
@Label("assign b") b = 10;
e.f.g = false;
}
The label annotation is used as the state name in the generated ASL. Label annotations can also be added before if statements
and before each case keyword of the when construct. Every stepper program has a special state derived by adding ".success"
to the name of the stepper program (assignment.success in our example above) that is the last step of the state machine and
is setup as a success
ASL state.
Stepper allows you to express loops and iterations more naturally without having to manually create cumbersome constructs in
ASL. Stepper supports the traditional for
loop, the iterative variant for in
and while
loops.
To loop over a range, use the for
loop
for (a = 0 to 10 step 2) {
<statement+>
}
The initial value, final value and step can all be expressions. Since Step Functions don't have variable scopes, the loop variable remains with its last value after the loop terminates.
for
loops are often used to iterate over collections. To loop over a javascript array, Stepper supports the for in
construct
for (n in names) {
<statement+>
}
The while
loop evaluates a boolean predicate and executes the body of the loop as long as the predicate is true.
while (x < s.length) {
<statement+>
}
Tasks are the mainstay of Step Functions. Tasks allow you to call activities, insert into SQS, read from Dynamo and make calls to Lambdas. Stepper provides features that makes using Task states easier. Here is an example of making a call to insert a value into a queue:
task {
"Resource": "arn:aws:states:::sqs:sendMessage",
"Parameters": {
"MessageBody": "0",
"QueueUrl": "https://sqs.us-east-1.amazonaws.com/1570xxx/fibo"
}
}
As you can see, the basic structure is very similar to the ASL construct. However, with Stepper tasks, you can get a return value by simple assignment:
entry = 5;
sqs = task {
"Resource": "arn:aws:states:::sqs:sendMessage",
"Parameters": {
"MessageBody.$": "$.entry",
"QueueUrl": "https://sqs.us-east-1.amazonaws.com/1570xxx/fibo"
}
}
The assignment creates a result path of sqs
that will contain the returned value. Calls to task should have a
return value assignement or a ResultPath
json element. Otherwise, an element with the same name as the state will
get the return value.
To give the task with a very specific state name, simply add the label annotation.
@Label("mySqsTask")
sqs = task {
"Resource": "arn:aws:states:::sqs:sendMessage",
"Parameters": {
"MessageBody.$": "$.entry",
"QueueUrl": "https://sqs.us-east-1.amazonaws.com/1570xxx/fibo"
}
}
The wait construct in Stepper is also a simplified version of the ASL state.
wait {
"Seconds": 10 // or "Timestamp": <constant or variable>
}
Like the wait
statement, the fail construct in Stepper is a simplified version of the ASL state.
fail {
"Cause": "Invalid response.",
"Error": "ErrorA"
}
As with wait
, you do not provide a state name. Stepper does not support the success step. To stop the
state machine from a particular step, either set the branching logic so that the particular step leads to the end
of the program or put in an explicit goto statement to special state of ".success".
Step Functions support declaring retry logic by error type. Stepper allows you to use annotations to describe the same.
stepper mytask {
@RetryOnError("abc", "def") { "IntervalSeconds": 3, "MaxAttempts": 4, "BackoffRate": 5 }
@RetryOnError("pqr") { "IntervalSeconds": 6 }
@Label("hello")
task {
...
}
}
The syntax for the retry annotation is
@RetryOnError("", "", ...) {
// encoding of IntervalSeconds
, MaxAttempts
and BackoffRate
as json attributes
}
Note: IntervalSeconds
is optional and defaults to 1, MaxAttempts
defaults to 3 and BackoffRate
defaults to 2.
try/catch construct for error handling.
Step Function Task
and Parallel
states allow for the definition a catcher using a "Catch" attribute. The
"Catch" attribute allows one to jump to a different state upon encountering a particular exception. Stepper models the
catcher using tradition try/catch construct.
stepper Simple {
a = 5;
try {
b = 3 * a;
d = b * b;
} catch ("e1", "e2") { errorInfo ->
c = 3;
goto "g1";
} catch ("e3", "e4") {
fail;
}
@Label("g1")
d = 10;
}
In the example above, two assignment statements have been wrapped with a try/catch construct. The catch keyword is followed by a list of errors to which it applies. As seen, you can have multiple catch clauses. The catch clause can also declare a variable (errorInfo in the example above) that will be propagated to the next statement. The catch clause must terminate with a goto or fail statement.
The Parallel
state in Step Functions allow you to declare two or more child step-functions that will be executed
in parallel. The syntax in ASL is particularly cumbersome as you have to embed the child step functions within the
Parallel
task's definition. Stepper allows you to write separate Stepper programs for each child step-function to be run
in parallel and invoke them by reference.
stepper Para {
a = 5 * 2;
@Label("a")
@RetryOnError("pqr") { "IntervalSeconds": 6 }
b = parallel("first.stepper", "second.stepper")
}
stepper First { // defined in file named first.stepper
a = 5 * 5;
b = 10;
}
stepper Second { // defined in file name second.stepper
a = 15 * 5;
b = 20;
}
In the example above, the Stepper program Para makes a parallel call to two other stepper programs defined in files
called first.stepper and second.stepper. Those files are expected to be in the same directory as Para. However, files can be
loaded from the JVM classpath by prefixing it with classpath://