Index
Overview
Statement = DefStmt | IfStmt | ForStmt | SimpleStmt .
SimpleStmt = SmallStmt {';' SmallStmt} [';'] '\n' .
SmallStmt = ReturnStmt
| BreakStmt | ContinueStmt | PassStmt
| AssignStmt
| ExprStmt
| LoadStmt
.
Pass statements
A pass
statement does nothing. Use a pass
statement when the
syntax requires a statement but no behavior is required, such as the
body of a function that does nothing.
PassStmt = 'pass' .
Example:
def noop():
pass
def list_to_dict(items):
# Convert list of tuples to dict
m = {}
for k, m[k] in items:
pass
return m
Assignments
An assignment statement has the form lhs = rhs
. It evaluates the
expression on the right-hand side then assigns its value (or values) to
the variable (or variables) on the left-hand side.
AssignStmt = Expression '=' Expression .
The expression on the left-hand side is called a target. The simplest target is the name of a variable, but a target may also have the form of an index expression, to update the element of a list or dictionary, or a dot expression, to update the field of an object:
k = 1
a[i] = v
m.f = ""
Compound targets may consist of a comma-separated list of subtargets, optionally surrounded by parentheses or square brackets, and targets may be nested arbitarily in this way. An assignment to a compound target checks that the right-hand value is a sequence with the same number of elements as the target. Each element of the sequence is then assigned to the corresponding element of the target, recursively applying the same logic. It is a static error if the sequence is empty.
pi, e = 3.141, 2.718
(x, y) = f()
[zero, one, two] = range(3)
[(a, b), (c, d)] = {"a": "b", "c": "d"}.items()
a, b = {"a": 1, "b": 2}
The same process for assigning a value to a target expression is used
in for
loops and in comprehensions.
Implementation note: In the Java implementation, targets cannot be dot expressions.
Augmented assignments
An augmented assignment, which has the form lhs op= rhs
updates the
variable lhs
by applying a binary arithmetic operator op
(one of
+
, -
, *
, /
, //
, %
, &
, |
, ^
, <<
, >>
) to the previous
value of lhs
and the value of rhs
.
AssignStmt = Expression ('+=' | '-=' | '*=' | '/=' | '//=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=') Expression .
The left-hand side must be a simple target: a name, an index expression, or a dot expression.
x -= 1
x.filename += ".star"
a[index()] *= 2
Any subexpressions in the target on the left-hand side are evaluated
exactly once, before the evaluation of rhs
.
The first two assignments above are thus equivalent to:
x = x - 1
x.filename = x.filename + ".star"
and the third assignment is similar in effect to the following two
statements but does not declare a new temporary variable i
:
i = index()
a[i] = a[i] * 2
Function definitions
A def
statement creates a named function and assigns it to a variable.
DefStmt = 'def' identifier '(' [Parameters [',']] ')' ':' Suite .
Example:
def twice(x):
return x * 2
str(twice) # "<function twice>"
twice(2) # 4
twice("two") # "twotwo"
The function’s name is preceded by the def
keyword and followed by
the parameter list (which is enclosed in parentheses), a colon, and
then an indented block of statements which form the body of the function.
The parameter list is a comma-separated list whose elements are of several kinds. First come zero or more required parameters, which are simple identifiers; all calls must provide an argument value for these parameters.
The required parameters are followed by zero or more optional
parameters, of the form name=expression
. The expression specifies
the default value for the parameter for use in calls that do not
provide an argument value for it.
The required parameters are optionally followed by a single parameter
name preceded by a *
. This is the called the varargs parameter,
and it accumulates surplus positional arguments specified by a call.
It is conventionally named *args
.
The varargs parameter may be followed by zero or more
parameters, again of the forms name
or name=expression
,
but these parameters differ from earlier ones in that they are
keyword-only: if a call provides their values, it must do so as
keyword arguments, not positional ones.
def f(a, *, b=2, c):
print(a, b, c)
f(1) # error: function f missing 1 argument (c)
f(1, 3) # error: function f accepts 1 positional argument (2 given)
f(1, c=3) # "1 2 3"
def g(a, *args, b=2, c):
print(a, b, c, args)
g(1, 3) # error: function g missing 1 argument (c)
g(1, 4, c=3) # "1 2 3 (4,)"
A non-variadic function may also declare keyword-only parameters,
by using a bare *
in place of the *args
parameter.
This form does not declare a parameter but marks the boundary
between the earlier parameters and the keyword-only parameters.
This form must be followed by at least one optional parameter.
Finally, there may be an optional parameter name preceded by **
.
This is called the keyword arguments parameter, and accumulates in a
dictionary any surplus name=value
arguments that do not match a
prior parameter. It is conventionally named **kwargs
.
The final parameter may be followed by a trailing comma.
Here are some example parameter lists:
def f(): pass
def f(a, b, c): pass
def f(a, b, c=1): pass
def f(a, b, c=1, *args): pass
def f(a, b, c=1, *args, **kwargs): pass
def f(**kwargs): pass
def f(a, b, c=1, *, d=1): pass
def f(
a,
*args,
**kwargs,
)
Execution of a def
statement creates a new function object. The
function object contains: the syntax of the function body; the default
value for each optional parameter; the value of each free variable
referenced within the function body; and the global dictionary of the
current module.
Implementation note:
The Go implementation of Starlark requires the -nesteddef
flag to enable support for nested def
statements.
The Java implementation does not permit a def
expression to be
nested within the body of another function.
Return statements
A return
statement ends the execution of a function and returns a
value to the caller of the function.
ReturnStmt = 'return' [Expression] .
A return statement may have zero, one, or more
result expressions separated by commas.
With no expressions, the function has the result None
.
With a single expression, the function’s result is the value of that expression.
With multiple expressions, the function’s result is a tuple.
return # returns None
return 1 # returns 1
return 1, 2 # returns (1, 2)
Expression statements
An expression statement evaluates an expression and discards its result.
ExprStmt = Expression .
Any expression may be used as a statement, but an expression statement is most often used to call a function for its side effects.
list.append(1)
If statements
An if
statement evaluates an expression (the condition), then, if
the truth value of the condition is True
, executes a list of
statements.
IfStmt = 'if' Test ':' Suite {'elif' Test ':' Suite} ['else' ':' Suite] .
Example:
if score >= 100:
print("You win!")
return
An if
statement may have an else
block defining a second list of
statements to be executed if the condition is false.
if score >= 100:
print("You win!")
return
else:
print("Keep trying...")
continue
It is common for the else
block to contain another if
statement.
To avoid increasing the nesting depth unnecessarily, the else
and
following if
may be combined as elif
:
if x > 0:
result = +1
elif x < 0:
result = -1
else:
result = 0
An if
statement is permitted only within a function definition.
An if
statement at top level results in a static error.
Implementation note:
The Go implementation of Starlark permits if
-statements to appear at top level
if the -globalreassign
flag is enabled.
While loops
A while
loop evaluates an expression (the condition) and if the truth
value of the condition is True
, it executes a list of statement and repeats
the process until the truth value of the condition becomes False
.
WhileStmt = 'while' Test ':' Suite .
Example:
while n > 0:
r = r + n
n = n - 1
A while
statement is permitted only within a function definition.
A while
statement at top level results in a static error.
Implementation note:
The Go implementation of Starlark permits while
loops only if the -recursion
flag is enabled.
A while
statement is permitted at top level if the -globalreassign
flag is enabled.
For loops
A for
loop evaluates its operand, which must be an iterable value.
Then, for each element of the iterable’s sequence, the loop assigns
the successive element values to one or more variables and executes a
list of statements, the loop body.
ForStmt = 'for' LoopVariables 'in' Expression ':' Suite .
Example:
for x in range(10):
print(10)
The assignment of each value to the loop variables follows the same rules as an ordinary assignment. In this example, two-element lists are repeatedly assigned to the pair of variables (a, i):
for a, i in [["a", 1], ["b", 2], ["c", 3]]:
print(a, i) # prints "a 1", "b 2", "c 3"
Because Starlark loops always iterate over a finite sequence, they are guaranteed to terminate, unlike loops in most languages which can execute an arbitrary and perhaps unbounded number of iterations.
Within the body of a for
loop, break
and continue
statements may
be used to stop the execution of the loop or advance to the next
iteration.
In Starlark, a for
loop is permitted only within a function definition.
A for
loop at top level results in a static error.
Implementation note:
The Go implementation of Starlark permits loops to appear at top level
if the -globalreassign
flag is enabled.
Break and Continue
The break
and continue
statements terminate the current iteration
of a for
loop. Whereas the continue
statement resumes the loop at
the next iteration, a break
statement terminates the entire loop.
BreakStmt = 'break' .
ContinueStmt = 'continue' .
Example:
for x in range(10):
if x%2 == 1:
continue # skip odd numbers
if x > 7:
break # stop at 8
print(x) # prints "0", "2", "4", "6"
Both statements affect only the innermost lexically enclosing loop.
It is a static error to use a break
or continue
statement outside a
loop.
Load statements
The load
statement loads another Starlark module, extracts one or
more values from it, and binds them to names in the current module.
Syntactically, a load statement looks like a function call load(...)
.
LoadStmt = 'load' '(' string {',' [identifier '='] string} [','] ')' .
A load statement requires at least two “arguments”. The first must be a literal string; it identifies the module to load. Its interpretation is determined by the application into which the Starlark interpreter is embedded, and is not specified here.
During execution, the application determines what action to take for a load statement. A typical implementation locates and executes a Starlark file, populating a cache of files executed so far to avoid duplicate work, to obtain a module, which is a mapping from global names to values.
The remaining arguments are a mixture of literal strings, such as
"x"
, or named literal strings, such as y="x"
.
The literal string ("x"
), which must denote a valid identifier not
starting with _
, specifies the name to extract from the loaded
module. In effect, names starting with _
are not exported.
The name (y
) specifies the local name;
if no name is given, the local name matches the quoted name.
load("module.star", "x", "y", "z") # assigns x, y, and z
load("module.star", "x", y2="y", "z") # assigns x, y2, and z
A load statement within a function is a static error.