Remove spec.md page. Source of truth is https://github.com/bazelbuild/starlark/blob/master/spec.md RELNOTES: None. PiperOrigin-RevId: 215639594
diff --git a/site/docs/build-ref.html b/site/docs/build-ref.html index e175c14..dab6674 100644 --- a/site/docs/build-ref.html +++ b/site/docs/build-ref.html
@@ -411,8 +411,35 @@ <p> However, BUILD files are evaluated using an imperative language, + <a href="https://github.com/bazelbuild/starlark/">Starlark</a>. - <a href="skylark/language.html">Skylark</a>. + BUILD files are interpreted as a sequential list of statements. + Build rule functions, such as <code>cc_library</code>, are procedures whose + side-effect is to create an abstract build rule inside the build tool. +</p> + +<p> + To encourage a clean separatation between code and data, BUILD files cannot + contain function definitions, <code>for</code> statements or + <code>if</code> statements (but list comprehensions and <code>if</code> + expressions are allowed). Functions should be declared in <code>.bzl</code> + files instead. +</p> + +<p> + Crucially, programs in Skylark are unable to perform + arbitrary I/O. This invariant makes the + interpretation of BUILD files hermetic, i.e. dependent only on a + known set of inputs, which is essential for ensuring that builds are + reproducible. +</p> + +<p> + BUILD files should be written using only ASCII characters, + although technically they are interpreted using the Latin-1 + character set. +</p> + <h3 id="declaring_build_rules">Declaring build rules</h3> <p>
diff --git a/site/docs/skylark/spec.md b/site/docs/skylark/spec.md deleted file mode 100644 index 13cad17..0000000 --- a/site/docs/skylark/spec.md +++ /dev/null
@@ -1,3264 +0,0 @@ ---- -layout: documentation -title: Skylark Language Specification ---- - -# Skylark Language Specification - -Skylark is a dialect of Python intended for use as a configuration -language. A Skylark interpreter is typically embedded within a larger -application, and this application may define additional -domain-specific functions and data types beyond those provided by the -core language. For example, Skylark is embedded within (and was -originally developed for) the [Bazel build tool](https://bazel.build), -and [Bazel's build language](https://docs.bazel.build/versions/master/skylark/language.html) is based on Skylark. -Another implementation in Go can be found here: [https://github.com/google/skylark/](https://github.com/google/skylark/) - -This document was derived from the [description of the Go -implementation](https://github.com/google/skylark/blob/master/doc/spec.md) -of Skylark. -It was influenced by the Python specification, -Copyright 1990–2017, Python Software Foundation, -and the Go specification, Copyright 2009–2017, The Go Authors. It is -now maintained by the Bazel team. - -The name "Skylark" is a code name of the Bazel project. -We plan to rename the language soon to reflect its -applicability to projects unrelated to Bazel. - -<!-- [TOC] --> - -## Overview - -Skylark is an untyped dynamic language with high-level data types, -first-class functions with lexical scope, and automatic memory -management or _garbage collection_. - -Skylark is strongly influenced by Python, and is almost a subset of -that language. In particular, its data types and syntax for -statements and expressions will be very familiar to any Python -programmer. -However, Skylark is intended not for writing applications but for -expressing configuration: its programs are short-lived and have no -external side effects and their main result is structured data or side -effects on the host application. - -Skylark is intended to be simple. There are no user-defined types, no -inheritance, no reflection, no exceptions, no explicit memory management. -Execution is finite. The language does not allow recursion or unbounded loops. - -Skylark is suitable for use in highly parallel applications. An application may -invoke the Skylark interpreter concurrently from many threads, without the -possibility of a data race, because shared data structures become immutable due -to _freezing_. - -The language is deterministic and hermetic. Executing the same file with the -same interpreter leads to the same result. By default, user code cannot -interact with the environment. - - -## Lexical elements - -A Skylark program consists of one or more modules. Each module is defined by a -single UTF-8-encoded text file. - -Skylark grammar is introduced gradually throughout this document as shown below, -and a [complete Skylark grammar reference](#grammar) is provided at the end. - -Grammar notation: - -```text -- lowercase and 'quoted' items are lexical tokens. -- Capitalized names denote grammar productions. -- (...) implies grouping. -- x | y means either x or y. -- [x] means x is optional. -- {x} means x is repeated zero or more times. -- The end of each declaration is marked with a period. -``` - -The contents of a Skylark file are broken into a sequence of tokens of -five kinds: white space, punctuation, keywords, identifiers, and literals. -Each token is formed from the longest sequence of characters that -would form a valid token of each kind. - -```text -File = {Statement | newline} eof . -``` - -*White space* consists of spaces (U+0020), tabs (U+0009), carriage -returns (U+000D), and newlines (U+000A). Within a line, white space -has no effect other than to delimit the previous token, but newlines, -and spaces at the start of a line, are significant tokens. - -*Comments*: A hash character (`#`) appearing outside of a string -literal marks the start of a comment; the comment extends to the end -of the line, not including the newline character. -Comments are treated like other white space. - -*Punctuation*: The following punctuation characters or sequences of -characters are tokens: - -```text -+ - * // % ** -. , = ; : -( ) [ ] { } -< > >= <= == != -+= -= *= //= %= -``` - -*Keywords*: The following tokens are keywords and may not be used as -identifiers: - -```text -and else load -break for not -continue if or -def in pass -elif return -``` - -The tokens below also may not be used as identifiers although they do not -appear in the grammar; they are reserved as possible future keywords: - -<!-- and to remain a syntactic subset of Python --> - -```text -as is -assert lambda -class nonlocal -del raise -except try -finally while -from with -global yield -import -``` - -*Identifiers*: an identifier is a sequence of Unicode letters, decimal - digits, and underscores (`_`), not starting with a digit. -Identifiers are used as names for values. - -Examples: - -```text -None True len -x index starts_with arg0 -``` - -*Literals*: literals are tokens that denote specific values. Skylark -has string and integer literals. - -```text -0 # int -123 # decimal int -0x7f # hexadecimal int -0o755 # octal int - -"hello" 'hello' # string -'''hello''' """hello""" # triple-quoted string -r'hello' r"hello" # raw string literal -``` - -Integer literal tokens are defined by the following grammar: - -```text -int = decimal_lit | octal_lit | hex_lit | 0 . -decimal_lit = ('1' … '9') {decimal_digit} . -octal_lit = '0' ('o' | 'O') octal_digit {octal_digit} . -hex_lit = '0' ('x' | 'X') hex_digit {hex_digit} . - -decimal_digit = '0' … '9' . -octal_digit = '0' … '7' . -hex_digit = '0' … '9' | 'A' … 'F' | 'a' … 'f' . -``` - -TODO: define string_lit, indent, outdent, semicolon, newline, eof - -## Data types - -These are the main data types built in to the interpreter: - -```text -NoneType # the type of None -bool # True or False -int # a signed integer -string # a byte string -list # a fixed-length sequence of values -tuple # a fixed-length sequence of values, unmodifiable -dict # a mapping from values to values -function # a function -``` - -Some functions, such as the `range` function, return instances of -special-purpose types that don't appear in this list. -Additional data types may be defined by the host application into -which the interpreter is embedded, and those data types may -participate in basic operations of the language such as arithmetic, -comparison, indexing, and function calls. - -<!-- We needn't mention the stringIterable type here. --> - -Some operations can be applied to any Skylark value. For example, -every value has a type string that can be obtained with the expression -`type(x)`, and any value may be converted to a string using the -expression `str(x)`, or to a Boolean truth value using the expression -`bool(x)`. Other operations apply only to certain types. For -example, the indexing operation `a[i]` works only with strings, lists, -and tuples, and any application-defined types that are _indexable_. -The [_value concepts_](#value-concepts) section explains the groupings of -types by the operators they support. - - -### None - -`None` is a distinguished value used to indicate the absence of any other value. -For example, the result of a call to a function that contains no return statement is `None`. - -`None` is equal only to itself. Its [type](#type) is `"NoneType"`. -The truth value of `None` is `False`. - - -### Booleans - -There are two Boolean values, `True` and `False`, representing the -truth or falsehood of a predicate. The [type](#type) of a Boolean is `"bool"`. - -Boolean values are typically used as conditions in `if`-statements, -although any Skylark value used as a condition is implicitly -interpreted as a Boolean. -For example, the values `None`, `0`, and the empty sequences -`""`, `()`, `[]`, and `{}` have a truth value of `False`, whereas non-zero -numbers and non-empty sequences have a truth value of `True`. -Application-defined types determine their own truth value. -Any value may be explicitly converted to a Boolean using the built-in `bool` -function. - -```python -1 + 1 == 2 # True -2 + 2 == 5 # False - -if 1 + 1: - print("True") -else: - print("False") -``` - -### Integers - -The Skylark integer type represents integers. Its [type](#type) is `"int"`. - -Integers may be positive or negative. The precision is implementation-dependent. -It is a dynamic error if a result is outside the supported range. -Integers are totally ordered; comparisons follow mathematical -tradition. - -The `+` and `-` operators perform addition and subtraction, respectively. -The `*` operator performs multiplication. - -The `//` and `%` operations on integers compute floored division and -remainder of floored division, respectively. -If the signs of the operands differ, the sign of the remainder `x % y` -matches that of the dividend, `x`. -For all finite x and y (y ≠ 0), `(x // y) * y + (x % y) == x`. - -Any bool, number, or string may be interpreted as an integer by using -the `int` built-in function. - -An integer used in a Boolean context is considered true if it is -non-zero. - -```python -100 // 5 * 9 + 32 # 212 -3 // 2 # 1 -111111111 * 111111111 # 12345678987654321 -int("0xffff", 16) # 65535 -``` - - -### Strings - -A string represents an immutable sequence of bytes. -The [type](#type) of a string is `"string"`. - -Strings can represent arbitrary binary data, including zero bytes, but -most strings contain text, encoded by convention using UTF-8. - -The built-in `len` function returns the number of bytes in a string. - -Strings may be concatenated with the `+` operator. - -The substring expression `s[i:j]` returns the substring of `s` from -index `i` up to index `j`. The index expression `s[i]` returns the -1-byte substring `s[i:i+1]`. - -Strings are hashable, and thus may be used as keys in a dictionary. - -Strings are totally ordered lexicographically, so strings may be -compared using operators such as `==` and `<`. - -Strings are _not_ iterable sequences, so they cannot be used as the operand of -a `for`-loop, list comprehension, or any other operation than requires -an iterable sequence. - -Any value may formatted as a string using the `str` or `repr` built-in -functions, the `str % tuple` operator, or the `str.format` method. - -A string used in a Boolean context is considered true if it is -non-empty. - -Strings have several built-in methods: - -* [`capitalize`](#string·capitalize) -* [`count`](#string·count) -* [`endswith`](#string·endswith) -* [`find`](#string·find) -* [`format`](#string·format) -* [`index`](#string·index) -* [`isalnum`](#string·isalnum) -* [`isalpha`](#string·isalpha) -* [`isdigit`](#string·isdigit) -* [`islower`](#string·islower) -* [`isspace`](#string·isspace) -* [`istitle`](#string·istitle) -* [`isupper`](#string·isupper) -* [`join`](#string·join) -* [`lower`](#string·lower) -* [`lstrip`](#string·lstrip) -* [`partition`](#string·partition) -* [`replace`](#string·replace) -* [`rfind`](#string·rfind) -* [`rindex`](#string·rindex) -* [`rpartition`](#string·rpartition) -* [`rsplit`](#string·rsplit) -* [`rstrip`](#string·rstrip) -* [`split`](#string·split) -* [`splitlines`](#string·splitlines) -* [`startswith`](#string·startswith) -* [`strip`](#string·strip) -* [`title`](#string·title) -* [`upper`](#string·upper) - - -### Lists - -A list is a mutable sequence of values. -The [type](#type) of a list is `"list"`. - -Lists are indexable sequences: the elements of a list may be iterated -over by `for`-loops, list comprehensions, and various built-in -functions. - -List may be constructed using bracketed list notation: - -```python -[] # an empty list -[1] # a 1-element list -[1, 2] # a 2-element list -``` - -Lists can also be constructed from any iterable sequence by using the -built-in `list` function. - -The built-in `len` function applied to a list returns the number of elements. -The index expression `list[i]` returns the element at index i, -and the slice expression `list[i:j]` returns a new list consisting of -the elements at indices from i to j. - -List elements may be added using the `append` or `extend` methods, -removed using the `remove` method, or reordered by assignments such as -`list[i] = list[j]`. - -The concatenation operation `x + y` yields a new list containing all -the elements of the two lists x and y. - -For most types, `x += y` is equivalent to `x = x + y`, except that it -evaluates `x` only once, that is, it allocates a new list to hold -the concatenation of `x` and `y`. -However, if `x` refers to a list, the statement does not allocate a -new list but instead mutates the original list in place, similar to -`x.extend(y)`. - -Lists are not hashable, so may not be used in the keys of a dictionary. - -A list used in a Boolean context is considered true if it is -non-empty. - -A [_list comprehension_](#comprehensions) creates a new list whose elements are the -result of some expression applied to each element of another sequence. - -```python -[x*x for x in [1, 2, 3, 4]] # [1, 4, 9, 16] -``` - -A list value has these methods: - -* [`append`](#list·append) -* [`clear`](#list·clear) -* [`extend`](#list·extend) -* [`index`](#list·index) -* [`insert`](#list·insert) -* [`pop`](#list·pop) -* [`remove`](#list·remove) - -### Tuples - -A tuple is an immutable sequence of values. -The [type](#type) of a tuple is `"tuple"`. - -Tuples are constructed using parenthesized list notation: - -```python -() # the empty tuple -(1,) # a 1-tuple -(1, 2) # a 2-tuple ("pair") -(1, 2, 3) # a 3-tuple -``` - -Observe that for the 1-tuple, the trailing comma is necessary to -distinguish it from the parenthesized expression `(1)`. -1-tuples are seldom used. - -Skylark, unlike Python, does not permit a trailing comma to appear in -an unparenthesized tuple expression: - -```python -for k, v, in dict.items(): pass # syntax error at 'in' -_ = [(v, k) for k, v, in dict.items()] # syntax error at 'in' - -sorted(3, 1, 4, 1,) # ok -[1, 2, 3, ] # ok -{1: 2, 3:4, } # ok -``` - -Any iterable sequence may be converted to a tuple by using the -built-in `tuple` function. - -Like lists, tuples are indexed sequences, so they may be indexed and -sliced. The index expression `tuple[i]` returns the tuple element at -index i, and the slice expression `tuple[i:j]` returns a subsequence -of a tuple. - -Tuples are iterable sequences, so they may be used as the operand of a -`for`-loop, a list comprehension, or various built-in functions. - -Unlike lists, tuples cannot be modified. -However, the mutable elements of a tuple may be modified. - -Tuples are hashable (assuming their elements are hashable), -so they may be used as keys of a dictionary. - -Tuples may be concatenated using the `+` operator. - -A tuple used in a Boolean context is considered true if it is -non-empty. - - -### Dictionaries - -A dictionary is a mutable mapping from keys to values. -The [type](#type) of a dictionary is `"dict"`. - -Dictionaries provide constant-time operations to insert an element, to -look up the value for a key, or to remove an element. Dictionaries -are implemented using hash tables, so keys must be hashable. Hashable -values include `None`, Booleans, numbers, and strings, and tuples -composed from hashable values. Most mutable values, such as lists, -and dictionaries, are not hashable, unless they are frozen. -Attempting to use a non-hashable value as a key in a dictionary -results in a dynamic error, as does passing one to the built-in -`hash` function. - -A [dictionary expression](#dictionary-expressions) specifies a -dictionary as a set of key/value pairs enclosed in braces: - -```python -coins = { - "penny": 1, - "nickel": 5, - "dime": 10, - "quarter": 25, -} -``` - -The expression `d[k]`, where `d` is a dictionary and `k` is a key, -retrieves the value associated with the key. If the dictionary -contains no such item, the operation fails: - -```python -coins["penny"] # 1 -coins["dime"] # 10 -coins["silver dollar"] # error: key not found -``` - -The number of items in a dictionary `d` is given by `len(d)`. -A key/value item may be added to a dictionary, or updated if the key -is already present, by using `d[k]` on the left side of an assignment: - -```python -len(coins) # 4 -coins["shilling"] = 20 -len(coins) # 5, item was inserted -coins["shilling"] = 5 -len(coins) # 5, existing item was updated -``` - -A dictionary can also be constructed using a [dictionary -comprehension](#comprehension), which evaluates a pair of expressions, -the _key_ and the _value_, for every element of another iterable such -as a list. This example builds a mapping from each word to its length -in bytes: - -```python -words = ["able", "baker", "charlie"] -{x: len(x) for x in words} # {"charlie": 7, "baker": 5, "able": 4} -``` - -Dictionaries are iterable sequences, so they may be used as the -operand of a `for`-loop, a list comprehension, or various built-in -functions. -Iteration yields the dictionary's keys in the order in which they were -inserted; updating the value associated with an existing key does not -affect the iteration order. - -```python -x = dict([("a", 1), ("b", 2)]) # {"a": 1, "b": 2} -x.update([("a", 3), ("c", 4)]) # {"a": 3, "b": 2, "c": 4} -``` - -```python -for name in coins: - print(name, coins[name]) # prints "quarter 25", "dime 10", ... -``` - -Like all mutable values in Skylark, a dictionary can be frozen, and -once frozen, all subsequent operations that attempt to update it will -fail. - -A dictionary used in a Boolean context is considered true if it is -non-empty. - -The binary `+` operation may be applied to two dictionaries. It -yields a new dictionary whose elements are the union of the two -operands. If a key is present in both operands, the result contains -the value from the right operand. -<b>Note:</b> this feature is deprecated. Use the -`dict.update` method instead. - -Dictionaries may be compared for equality using `==` and `!=`. Two -dictionaries compare equal if they contain the same number of items -and each key/value item (k, v) found in one dictionary is also present -in the other. Dictionaries are not ordered; it is an error to compare -two dictionaries with `<`. - - -A dictionary value has these methods: - -* [`clear`](#dict·clear) -* [`get`](#dict·get) -* [`items`](#dict·items) -* [`keys`](#dict·keys) -* [`pop`](#dict·pop) -* [`popitem`](#dict·popitem) -* [`setdefault`](#dict·setdefault) -* [`update`](#dict·update) -* [`values`](#dict·values) - - -### Functions - -A function value represents a function defined in Skylark. -Its [type](#type) is `"function"`. -A function value used in a Boolean context is always considered true. - -Function definitions may not be nested. - -A function definition defines zero or more named parameters. -Skylark has a rich mechanism for passing arguments to functions. - -<!-- TODO break up this explanation into caller-side and callee-side - parts, and put the former under function calls and the latter - under function definitions. Also try to convey that the Callable - interface sees the flattened-out args and kwargs and that's what - built-ins get. ---> - -The example below shows a definition and call of a function of two -required parameters, `x` and `y`. - -```python -def idiv(x, y): - return x // y - -idiv(6, 3) # 2 -``` - -A call may provide arguments to function parameters either by -position, as in the example above, or by name, as in first two calls -below, or by a mixture of the two forms, as in the third call below. -All the positional arguments must precede all the named arguments. -Named arguments may improve clarity, especially in functions of -several parameters. - -```python -idiv(x=6, y=3) # 2 -idiv(y=3, x=6) # 2 - -idiv(6, y=3) # 2 -``` - -<b>Optional parameters:</b> A parameter declaration may specify a -default value using `name=value` syntax; such a parameter is -_optional_. The default value expression is evaluated during -execution of the `def` statement, and the default value forms part of the function value. -All optional parameters must follow all non-optional parameters. -A function call may omit arguments for any suffix of the optional -parameters; the effective values of those arguments are supplied by -the function's parameter defaults. - -```python -def f(x, y=3): - return x, y - -f(1, 2) # (1, 2) -f(1) # (1, 3) -``` - -If a function parameter's default value is a mutable expression, -modifications to the value during one call may be observed by -subsequent calls. -Beware of this when using lists or dicts as default values. -If the function becomes frozen, its parameters' default values become -frozen too. - -```python -# module a.sky -def f(x, list=[]): - list.append(x) - return list - -f(4, [1,2,3]) # [1, 2, 3, 4] -f(1) # [1] -f(2) # [1, 2], not [2]! - -# module b.sky -load("a.sky", "f") -f(3) # error: cannot append to frozen list -``` - -<b>Variadic functions:</b> Some functions allow callers to provide an -arbitrary number of arguments. -After all required and optional parameters, a function definition may -specify a _variadic arguments_ or _varargs_ parameter, indicated by a -star preceding the parameter name: `*args`. -Any surplus positional arguments provided by the caller are formed -into a tuple and assigned to the `args` parameter. - -```python -def f(x, y, *args): - return x, y, args - -f(1, 2) # (1, 2, ()) -f(1, 2, 3, 4) # (1, 2, (3, 4)) -``` - -<b>Keyword-variadic functions:</b> Some functions allow callers to -provide an arbitrary sequence of `name=value` keyword arguments. -A function definition may include a final _keyworded arguments_ or -_kwargs_ parameter, indicated by a double-star preceding the parameter -name: `**kwargs`. -Any surplus named arguments that do not correspond to named parameters -are collected in a new dictionary and assigned to the `kwargs` parameter: - -```python -def f(x, y, **kwargs): - return x, y, kwargs - -f(1, 2) # (1, 2, {}) -f(x=2, y=1) # (2, 1, {}) -f(x=2, y=1, z=3) # (2, 1, {"z": 3}) -``` - -It is a static error if any two parameters of a function have the same name. - -Just as a function definition may accept an arbitrary number of -positional or keyworded arguments, a function call may provide an -arbitrary number of positional or keyworded arguments supplied by a -list or dictionary: - -```python -def f(a, b, c=5): - return a * b + c - -f(*[2, 3]) # 11 -f(*[2, 3, 7]) # 13 -f(*[2]) # error: f takes at least 2 arguments (1 given) - -f(**dict(b=3, a=2)) # 11 -f(**dict(c=7, a=2, b=3)) # 13 -f(**dict(a=2)) # error: f takes at least 2 arguments (1 given) -f(**dict(d=4)) # error: f got unexpected keyword argument "d" -``` - -Once the parameters have been successfully bound to the arguments -supplied by the call, the sequence of statements that comprise the -function body is executed. - -A function call completes normally after the execution of either a -`return` statement, or of the last statement in the function body. -The result of the function call is the value of the return statement's -operand, or `None` if the return statement had no operand or if the -function completeted without executing a return statement. - -```python -def f(x): - if x == 0: - return - if x < 0: - return -x - print(x) - -f(1) # returns None after printing "1" -f(0) # returns None without printing -f(-1) # returns 1 without printing -``` - - -It is a dynamic error for a function to call itself or another -function value with the same declaration. - -```python -def fib(x): - if x < 2: - return x - return fib(x-2) + fib(x-1) # dynamic error: function fib called recursively - -fib(5) -``` - -This rule, combined with the invariant that all loops are iterations -over finite sequences, implies that Skylark programs are not Turing-complete. - -<!-- This rule is supposed to deter people from abusing Skylark for - inappropriate uses, especially in the build system. - It may work for that purpose, but it doesn't stop Skylark programs - from consuming too much time or space. Perhaps it should be a - dialect option. ---> - - - -### Built-in functions - -A built-in function is a function or method implemented by the interpreter -or the application into which the interpreter is embedded. - -A built-in function value used in a Boolean context is always considered true. - -Many built-in functions are defined in the "universe" block of the environment -(see [Name Resolution](#name-resolution)), and are thus available to -all Skylark programs. - -Except where noted, built-in functions accept only positional arguments. - -## Name binding and variables - -After a Skylark file is parsed, but before its execution begins, the -Skylark interpreter checks statically that the program is well formed. -For example, `break` and `continue` statements may appear only within -a loop; `if`, `for`, and `return` statements may appear only within a -function; and `load` statements may appear only outside any function. - -_Name resolution_ is the static checking process that -resolves names to variable bindings. -During execution, names refer to variables. Statically, names denote -places in the code where variables are created; these places are -called _bindings_. A name may denote different bindings at different -places in the program. The region of text in which a particular name -refers to the same binding is called that binding's _scope_. - -Four Skylark constructs bind names, as illustrated in the example below: -`load` statements (`a` and `b`), -`def` statements (`c`), -function parameters (`d`), -and assignments (`e`, `h`, including the augmented assignment `e += h`). -Variables may be assigned or re-assigned explicitly (`e`, `h`), or implicitly, as -in a `for`-loop (`f`) or comprehension (`g`, `i`). - -```python -load("lib.sky", "a", b="B") - -def c(d): - e = 0 - for f in d: - print([True for g in f]) - e += 1 - -h = [2*i for i in a] -``` - -The environment of a Skylark program is structured as a tree of -_lexical blocks_, each of which may contain name bindings. -The tree of blocks is parallel to the syntax tree. -Blocks are of four kinds. - -<!-- Avoid the term "built-in block" since that's also a type. --> -At the root of the tree is the _universe_ block, which binds constant -values such as `None`, `True`, and `False`, and built-in functions -such as `len`, `list`, and so on. -Skylark programs cannot change the set of universe bindings. -Because the universe block is shared by all files (modules), -all values bound in it must be immutable and stateless -from the perspective of the Skylark program. - -Nested beneath the universe block is the _module_ block, which -contains the bindings of the current file. -Bindings in the module block (such as `a`, `b`, `c`, and `h` in the -example) are called _global_. -The module block is typically empty at the start of the file -and is populated by top-level binding statements, -but an application may pre-bind one or more global names, -to provide domain-specific functions to that file, for example. - -A module block contains a _function_ block for each top-level -function, and a _comprehension_ block for each top-level -comprehension. -Bindings inside either of these kinds of block are called _local_. -Additional functions and comprehensions, and their blocks, may be -nested in any order, to any depth. - -If name is bound anywhere within a block, all uses of the name within -the block are treated as references to that binding, even uses that -appear before the binding. -The binding of `y` on the last line of the example below makes `y` -local to the function `hello`, so the use of `y` in the print -statement also refers to the local `y`, even though it appears -earlier. - -```python -y = "goodbye" - -def hello(): - for x in (1, 2): - if x == 2: - print(y) # prints "hello" - if x == 1: - y = "hello" -``` - -It is a dynamic error to evaluate a reference to a local variable -before it has been bound: - -```python -def f(): - print(x) # dynamic error: local variable x referenced before assignment - x = "hello" -``` - -The same is true for global variables: - -```python -print(x) # dynamic error: global variable x referenced before assignment -x = "hello" -``` - -It is a static error to bind a global variable already explicitly bound in the file: - -```python -x = 1 -x = 2 # static error: cannot reassign global x declared on line 1 -``` - -If a name was pre-bound by the application, the Skylark program may -explicitly bind it, but only once. - -An augmented assignment statement such as `x += 1` is considered a -binding of `x`. It is therefore a static error to use it on a global variable. - -A name appearing after a dot, such as `split` in -`get_filename().split('/')`, is not resolved statically. -The [dot expression](#dot-expressions) `.split` is a dynamic operation -on the value returned by `get_filename()`. - - -## Value concepts {#value-concepts} - -Skylark has eleven core [data types](#data-types). An application -that embeds the Skylark intepreter may define additional types that -behave like Skylark values. All values, whether core or -application-defined, implement a few basic behaviors: - -```text -str(x) -- return a string representation of x -type(x) -- return a string describing the type of x -bool(x) -- convert x to a Boolean truth value -hash(x) -- return a hash code for x -``` - -### Identity and mutation - -Skylark is an imperative language: programs consist of sequences of -statements executed for their side effects. -For example, an assignment statement updates the value held by a -variable, and calls to some built-in functions such as `print` change -the state of the application that embeds the interpreter. - -Values of some data types, such as `NoneType`, `bool`, `int`, and -`string`, are _immutable_; they can never change. -Immutable values have no notion of _identity_: it is impossible for a -Skylark program to tell whether two integers, for instance, are -represented by the same object; it can tell only whether they are -equal. - -Values of other data types, such as `list` and `dict`, are -_mutable_: they may be modified by a statement such as `a[i] = 0` or -`items.clear()`. Although `tuple` and `function` values are not -directly mutable, they may refer to mutable values indirectly, so for -this reason we consider them mutable too. Skylark values of these -types are actually _references_ to variables. - -Copying a reference to a variable, using an assignment statement for -instance, creates an _alias_ for the variable, and the effects of -operations applied to the variable through one alias are visible -through all others. - -```python -x = [] # x refers to a new empty list variable -y = x # y becomes an alias for x -x.append(1) # changes the variable referred to by x -print(y) # "[1]"; y observes the mutation -``` - -Skylark uses _call-by-value_ parameter passing: in a function call, -argument values are assigned to function parameters as if by -assignment statements. If the values are references, the caller and -callee may refer to the same variables, so if the called function -changes the variable referred to by a parameter, the effect may also -be observed by the caller: - -```python -def f(y): - y.append(1) # changes the variable referred to by x - -x = [] # x refers to a new empty list variable -f(x) # f's parameter y becomes an alias for x -print(x) # "[1]"; x observes the mutation -``` - - -As in all imperative languages, understanding _aliasing_, the -relationship between reference values and the variables to which they -refer, is crucial to writing correct programs. - -### Freezing a value - -Skylark has a feature unusual among imperative programming languages: -a mutable value may be _frozen_ so that all subsequent attempts to -mutate it fail with a dynamic error; the value, and all other values -reachable from it, become _immutable_. - -Immediately after execution of a Skylark module, all values in its -top-level environment are frozen. Because all the global variables of -an initialized Skylark module are immutable, the module may be published to -and used by other threads in a parallel program without the need for -locks. For example, the Bazel build system loads and executes BUILD -and .bzl files in parallel, and two modules being executed -concurrently may freely access variables or call functions from a -third without the possibility of a race condition. - -### Hashing - -The `dict` data type is implemented using hash tables, so -only _hashable_ values are suitable as keys of a `dict`. -Attempting to use a non-hashable value as the key in a hash -table, or as the operand of the `hash` built-in function, results in a -dynamic error. - -The hash of a value is an unspecified integer chosen so that two equal -values have the same hash, in other words, `x == y => hash(x) == hash(y)`. -A hashable value has the same hash throughout its lifetime. - -Values of the types `NoneType`, `bool`, `int`, and `string`, -which are all immutable, are hashable. - -Values of mutable types such as `list` and `dict` are not -hashable, unless they have become immutable due to _freezing_. - -A `tuple` value is hashable only if all its elements are hashable. -Thus `("localhost", 80)` is hashable but `([127, 0, 0, 1], 80)` is not. - -Values of type `function` are also hashable. -Although functions are not necessarily immutable, as they may be -closures that refer to mutable variables, instances of these types -are compared by reference identity (see [Comparisons](#comparisons)), -so their hash values are derived from their identity. - - -### Sequence types - -Many Skylark data types represent a _sequence_ of values: lists -and tuples are sequences of arbitrary values, and in many -contexts dictionaries act like a sequence of their keys. - -We can classify different kinds of sequence types based on the -operations they support. - -* `Iterable`: an _iterable_ value lets us process each of its elements in a fixed order. - Examples: `dict`, `list`, `tuple`, but not `string`. -* `Sequence`: a _sequence of known length_ lets us know how many elements it - contains without processing them. - Examples: `dict`, `list`, `tuple`, but not `string`. -* `Indexable`: an _indexed_ type has a fixed length and provides efficient - random access to its elements, which are identified by integer indices. - Examples: `string`, `tuple`, and `list`. -* `SetIndexable`: a _settable indexed type_ additionally allows us to modify the - element at a given integer index. Example: `list`. -* `Mapping`: a mapping is an association of keys to values. Example: `dict`. - -Although all of Skylark's core data types for sequences implement at -least the `Sequence` contract, it's possible for an an application -that embeds the Skylark interpreter to define additional data types -representing sequences of unknown length that implement only the `Iterable` contract. - -Strings are not iterable, though they do support the `len(s)` and -`s[i]` operations. Skylark deviates from Python here to avoid common -pitfall in which a string is used by mistake where a list containing a -single string was intended, resulting in its interpretation as a sequence -of bytes. - -Most Skylark operators and built-in functions that need a sequence -of values will accept any iterable. - -It is a dynamic error to mutate a sequence such as a list or a -dictionary while iterating over it. - -```python -def increment_values(dict): - for k in dict: - dict[k] += 1 # error: cannot insert into hash table during iteration - -dict = {"one": 1, "two": 2} -increment_values(dict) -``` - - -### Indexing - -Many Skylark operators and functions require an index operand `i`, -such as `a[i]` or `list.insert(i, x)`. Others require two indices `i` -and `j` that indicate the start and end of a subsequence, such as -`a[i:j]`, `list.index(x, i, j)`, or `string.find(x, i, j)`. -All such operations follow similar conventions, described here. - -Indexing in Skylark is *zero-based*. The first element of a string -or list has index 0, the next 1, and so on. The last element of a -sequence of length `n` has index `n-1`. - -```python -"hello"[0] # "h" -"hello"[4] # "o" -"hello"[5] # error: index out of range -``` - -For subsequence operations that require two indices, the first is -_inclusive_ and the second _exclusive_. Thus `a[i:j]` indicates the -sequence starting with element `i` up to but not including element -`j`. The length of this subsequence is `j-i`. This convention is known -as *half-open indexing*. - -```python -"hello"[1:4] # "ell" -``` - -Either or both of the index operands may be omitted. If omitted, the -first is treated equivalent to 0 and the second is equivalent to the -length of the sequence: - -```python -"hello"[1:] # "ello" -"hello"[:4] # "hell" -``` - -It is permissible to supply a negative integer to an indexing -operation. The effective index is computed from the supplied value by -the following two-step procedure. First, if the value is negative, the -length of the sequence is added to it. This provides a convenient way -to address the final elements of the sequence: - -```python -"hello"[-1] # "o", like "hello"[4] -"hello"[-3:-1] # "ll", like "hello"[2:4] -``` - -Second, for subsequence operations, if the value is still negative, it -is replaced by zero, or if it is greater than the length `n` of the -sequence, it is replaced by `n`. In effect, the index is "truncated" to -the nearest value in the range `[0:n]`. - -```python -"hello"[-1000:1000] # "hello" -``` - -This truncation step does not apply to indices of individual elements: - -```python -"hello"[-6] # error: index out of range -"hello"[-5] # "h" -"hello"[4] # "o" -"hello"[5] # error: index out of range -``` - - -## Expressions - -An expression specifies the computation of a value. - -The Skylark grammar defines several categories of expression. -An _operand_ is an expression consisting of a single token (such as an -identifier or a literal), or a bracketed expression. -Operands are self-delimiting. -An operand may be followed by any number of dot, call, or slice -suffixes, to form a _primary_ expression. -In some places in the Skylark grammar where an expression is expected, -it is legal to provide a comma-separated list of expressions denoting -a tuple. -The grammar uses `Expression` where a multiple-component expression is allowed, -and `Test` where it accepts an expression of only a single component. - -```text -Expression = Test {',' Test} . - -Test = IfExpr | PrimaryExpr | UnaryExpr | BinaryExpr . - -PrimaryExpr = Operand - | PrimaryExpr DotSuffix - | PrimaryExpr CallSuffix - | PrimaryExpr SliceSuffix - . - -Operand = identifier - | int | string - | ListExpr | ListComp - | DictExpr | DictComp - | '(' [Expression] [,] ')' - | '-' PrimaryExpr - . - -DotSuffix = '.' identifier . -CallSuffix = '(' [Arguments [',']] ')' . -SliceSuffix = '[' [Expression] [':' Test [':' Test]] ']' . -``` - -### Identifiers - -```text -Primary = identifier -``` - -An identifier is a name that identifies a value. - -Lookup of locals and globals may fail if not yet defined. - -### Literals - -Skylark supports string literals of three different kinds: - -```text -Primary = int | string -``` - -Evaluation of a literal yields a value of the given type (string, int) with the -given value. -See [Literals](#lexical elements) for details. - -### Parenthesized expressions - -```text -Primary = '(' [Expression] ')' -``` - -A single expression enclosed in parentheses yields the result of that expression. -Explicit parentheses may be used for clarity, -or to override the default association of subexpressions. - -```python -1 + 2 * 3 + 4 # 11 -(1 + 2) * (3 + 4) # 21 -``` - -If the parentheses are empty, or contain a single expression followed -by a comma, or contain two or more expressions, the expression yields a tuple. - -```python -() # (), the empty tuple -(1,) # (1,), a tuple of length 1 -(1, 2) # (1, 2), a 2-tuple or pair -(1, 2, 3) # (1, 2, 3), a 3-tuple or triple -``` - -In some contexts, such as a `return` or assignment statement or the -operand of a `for` statement, a tuple may be expressed without -parentheses. - -```python -x, y = 1, 2 - -return 1, 2 - -for x in 1, 2: - print(x) -``` - -Skylark (like Python 3) does not accept an unparenthesized tuple -expression as the operand of a list comprehension: - -```python -[2*x for x in 1, 2, 3] # parse error: unexpected ',' -``` - -### Dictionary expressions - -A dictionary expression is a comma-separated list of colon-separated -key/value expression pairs, enclosed in curly brackets, and it yields -a new dictionary object. -An optional comma may follow the final pair. - -```text -DictExpr = '{' [Entries [',']] '}' . -Entries = Entry {',' Entry} . -Entry = Test ':' Test . -``` - -Examples: - - -```python -{} -{"one": 1} -{"one": 1, "two": 2,} -``` - -The key and value expressions are evaluated in left-to-right order. -Evaluation fails if the same key is used multiple times. - -Only [hashable](#hashing) values may be used as the keys of a dictionary. - - -### List expressions - -A list expression is a comma-separated list of element expressions, -enclosed in square brackets, and it yields a new list object. -An optional comma may follow the last element expression. - -```text -ListExpr = '[' [Expression [',']] ']' . -``` - -Element expressions are evaluated in left-to-right order. - -Examples: - -```python -[] # [], empty list -[1] # [1], a 1-element list -[1, 2, 3,] # [1, 2, 3], a 3-element list -``` - -### Unary operators - -There are two unary operators, both appearing before their operand: -`-`, and `not`. - -```text -UnaryExpr = '-' Test - | 'not' Test - . -``` - -```text -- number unary negation (number) -not x logical negation (any type) -``` - -The `-` operators returns the opposite of any number. - -```python -if x > 0: - return 1 -elif x < 0: - return -1 -else: - return 0 -``` - -The `not` operator returns the negation of the truth value of its -operand. - -```python -not True # False -not False # True -not [1, 2, 3] # False -not "" # True -not 0 # True -``` - - -### Binary operators - -Skylark has the following binary operators, arranged in order of increasing precedence: - -```text -or -and -not -== != < > <= >= in not in -- + -* / // % -``` - -Comparison operators, `in`, and `not in` are non-associative, -so the parser will not accept `0 <= i < n`. -All other binary operators of equal precedence associate to the left. - -```text -BinaryExpr = Test {Binop Test} . - -Binop = 'or' - | 'and' - | 'not' - | '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in' | 'not' 'in' - | '-' | '+' - | '*' | '%' | '/' | '//' - . -``` - -#### `or` and `and` - -The `or` and `and` operators yield, respectively, the logical disjunction and -conjunction of their arguments, which need not be Booleans. -The expression `x or y` yields the value of `x` if its truth value is `True`, -or the value of `y` otherwise. - -```python -False or False # False -False or True # True -True or False # True -True or True # True - -0 or "hello" # "hello" -1 or "hello" # 1 -``` - -Similarly, `x and y` yields the value of `x` if its truth value is -`False`, or the value of `y` otherwise. - -```python -False and False # False -False and True # False -True and False # False -True and True # True - -0 and "hello" # 0 -1 and "hello" # "hello" -``` - -These operators use "short circuit" evaluation, so the second -expression is not evaluated if the value of the first expression has -already determined the result, allowing constructions like these: - -```python -len(x) > 0 and x[0] == 1 # x[0] is not evaluated if x is empty -x and x[0] == 1 -len(x) == 0 or x[0] == "" -not x or not x[0] -``` - -#### Comparisons - -The `==` operator reports whether its operands are equal; the `!=` -operator is its negation. - -The operators `<`, `>`, `<=`, and `>=` perform an ordered comparison -of their operands. It is an error to apply these operators to -operands of unequal type. Of the built-in types, only the following -support ordered comparison, using the ordering relation shown: - -```text -NoneType # None <= None -bool # False < True -int # mathematical -string # lexicographical -tuple # lexicographical -list # lexicographical -``` - -Applications may define additional types that support ordered -comparison. - -The remaining built-in types support only equality comparisons. -Values of type `dict` compare equal if their elements compare -equal, and values of type `function` are equal only to themselves. - -```text -dict # equal contents -function # identity -``` - -#### Arithmetic operations - -The following table summarizes the binary arithmetic operations -available for built-in types: - -```text -Arithmetic - number + number # addition - number - number # subtraction - number * number # multiplication - number // number # floored division - number % number # remainder of floored division - -Concatenation - string + string - list + list - tuple + tuple - dict + dict # (deprecated) - -Repetition (string/list/tuple) - int * sequence - sequence * int - -String interpolation - string % any # see String Interpolation -``` - -The operands of the arithmetic operators `+`, `-`, `*`, `//`, and -`%` must both be `int`. The type of the result has type `int`. - -The `+` operator may be applied to non-numeric operands of the same -type, such as two lists, two tuples, or two strings, in which case it -computes the concatenation of the two operands and yields a new value of -the same type. - -```python -"Hello, " + "world" # "Hello, world" -(1, 2) + (3, 4) # (1, 2, 3, 4) -[1, 2] + [3, 4] # [1, 2, 3, 4] -``` - -The `*` operator may be applied to an integer _n_ and a value of type -`string`, `list`, or `tuple`, in which case it yields a new value -of the same sequence type consisting of _n_ repetitions of the original sequence. -The order of the operands is immaterial. -Negative values of _n_ behave like zero. - -```python -'mur' * 2 # 'murmur' -3 * range(3) # [0, 1, 2, 0, 1, 2, 0, 1, 2] -``` - -Applications may define additional types that support any subset of -these operators. - - -#### Membership tests - -```text - any in sequence (list, tuple, dict, string) - any not in sequence -``` - -The `in` operator reports whether its first operand is a member of its -second operand, which must be a list, tuple, dict, or string. -The `not in` operator is its negation. -Both return a Boolean. - -The meaning of membership varies by the type of the second operand: -the members of a list or tuple are its elements; -the members of a dict are its keys; -the members of a string are all its substrings. - -```python -1 in [1, 2, 3] # True -4 not in (1, 2, 3) # True - -d = {"one": 1, "two": 2} -"one" in d # True -"three" in d # False -1 in d # False - -"nasty" in "dynasty" # True -"a" in "banana" # True -"f" not in "way" # True -``` - -#### String interpolation - -The expression `format % args` performs _string interpolation_, a -simple form of template expansion. -The `format` string is interpreted as a sequence of literal portions -and _conversions_. -Each conversion, which starts with a `%` character, is replaced by its -corresponding value from `args`. -The characters following `%` in each conversion determine which -argument it uses and how to convert it to a string. - -Each `%` character marks the start of a conversion specifier, unless -it is immediately followed by another `%`, in which cases both -characters together denote a single literal percent sign. - -The conversion's operand is the next element of `args`, -which must be a tuple with exactly one component per conversion, -unless the format string contains only a single conversion, in which -case `args` itself is its operand. - -Skylark does not support the flag, width, and padding specifiers -supported by Python's `%` and other variants of C's `printf`. - -After the `%` comes a single letter indicating what -operand types are valid and how to convert the operand `x` to a string: - -```text -% none literal percent sign -s any as if by str(x) -r any as if by repr(x) -d number signed integer decimal -``` - -It is an error if the argument does not have the type required by the -conversion specifier. A Boolean argument is not considered a number. - -Examples: - -```python -"Hello %s" % "Bob" # "Hello Bob" - -"Hello %s, your score is %d" % ("Bob", 75) # "Hello Bob, your score is 75" -) -``` - -One subtlety: to use a tuple as the operand of a conversion in format -string containing only a single conversion, you must wrap the tuple in -a singleton tuple: - -```python -"coordinates=%s" % (40, -74) # error: too many arguments for format string -"coordinates=%s" % ((40, -74),) # "coordinates=(40, -74)" -``` - -### Conditional expressions - -A conditional expression has the form `a if cond else b`. -It first evaluates the condition `cond`. -If it's true, it evaluates `a` and yields its value; -otherwise it yields the value of `b`. - -```text -IfExpr = Test 'if' Test 'else' Test . -``` - -Example: - -```python -"yes" if enabled else "no" -``` - -### Comprehensions - -A comprehension constructs new list or dictionary value by looping -over one or more iterables and evaluating a _body_ expression that produces -successive elements of the result. - -A list comprehension consists of a single expression followed by one -or more _clauses_, the first of which must be a `for` clause. -Each `for` clause resembles a `for` statement, and specifies an -iterable operand and a set of variables to be assigned by successive -values of the iterable. -An `if` cause resembles an `if` statement, and specifies a condition -that must be met for the body expression to be evaluated. -A sequence of `for` and `if` clauses acts like a nested sequence of -`for` and `if` statements. - -```text -ListComp = '[' Test {CompClause} ']'. -DictComp = '{' Entry {CompClause} '}' . - -CompClause = 'for' LoopVariables 'in' Test - | 'if' Test . - -LoopVariables = PrimaryExpr {',' PrimaryExpr} . -``` - -Examples: - -```python -[x*x for x in range(5)] # [0, 1, 4, 9, 16] -[x*x for x in range(5) if x%2 == 0] # [0, 4, 16] -[(x, y) for x in range(5) - if x%2 == 0 - for y in range(5) - if y > x] # [(0, 1), (0, 2), (0, 3), (0, 4), (2, 3), (2, 4)] -``` - -A dict comprehension resembles a list comprehension, but its body is a -pair of expressions, `key: value`, separated by a colon, -and its result is a dictionary containing the key/value pairs -for which the body expression was evaluated. -Evaluation fails if the value of any key is unhashable. - -As with a `for` loop, the loop variables may exploit compound -assignment: - -```python -[x*y+z for (x, y), z in [((2, 3), 5), (("o", 2), "!")]] # [11, 'oo!'] -``` - -Skylark, following Python 3, does not accept an unparenthesized -tuple as the operand of a `for` clause: - -```python -[x*x for x in 1, 2, 3] # parse error: unexpected comma -``` - -Comprehensions in Skylark, again following Python 3, define a new lexical -block, so assignments to loop variables have no effect on variables of -the same name in an enclosing block: - -```python -x = 1 -_ = [x for x in [2]] # new variable x is local to the comprehension -print(x) # 1 -``` - - -### Function and method calls - -```text -CallSuffix = '(' [Arguments [',']] ')' . - -Arguments = Argument {',' Argument} . -Argument = Test | identifier '=' Test | '*' Test | '**' Test . -``` - -A value `f` of type `function` may be called using the expression `f(...)`. -Applications may define additional types whose values may be called in the same way. - -A method call such as `filename.endswith(".sky")` is the composition -of two operations, `m = filename.endswith` and `m(".sky")`. -The first, a dot operation, yields a _bound method_, a function value -that pairs a receiver value (the `filename` string) with a choice of -method ([string·endswith](#string·endswith)). - -Only built-in or application-defined types may have methods. - -See [Functions](#functions) for an explanation of function parameter passing. - -### Dot expressions - -A dot expression `x.f` selects the attribute `f` (a field or method) -of the value `x`. - -Fields are possessed by none of the main Skylark [data types](#data-types), -but some application-defined types have them. -Methods belong to the built-in types `string`, `list`, and `dict`, -and to many application-defined types. - -```text -DotSuffix = '.' identifier . -``` - -A dot expression fails if the value does not have an attribute of the -specified name. - -Use the built-in function `hasattr(x, "f")` to ascertain whether a -value has a specific attribute, or `dir(x)` to enumerate all its -attributes. The `getattr(x, "f")` function can be used to select an -attribute when the name `"f"` is not known statically. - -A dot expression that selects a method typically appears within a call -expression, as in these examples: - -```python -["able", "baker", "charlie"].index("baker") # 1 -"banana".count("a") # 3 -"banana".reverse() # error: string has no .reverse field or method -``` - -But when not called immediately, the dot expression evaluates to a -_bound method_, that is, a method coupled to a specific receiver -value. A bound method can be called like an ordinary function, -without a receiver argument: - -```python -f = "banana".count -f # <built-in method count of string value> -f("a") # 3 -f("n") # 2 -``` - -<b>Implementation note:</b> -The Java implementation does not currently allow a method to be -selected but not immediately called. -See Google Issue b/21392896. - -### Index expressions - -An index expression `a[i]` yields the `i`th element of an _indexable_ -type such as a string, tuple, or list. The index `i` must be an `int` -value in the range -`n` ≤ `i` < `n`, where `n` is `len(a)`; any other -index results in an error. - -```text -SliceSuffix = '[' [Expression] [':' Test [':' Test]] ']' . -``` - -A valid negative index `i` behaves like the non-negative index `n+i`, -allowing for convenient indexing relative to the end of the -sequence. - -```python -"abc"[0] # "a" -"abc"[1] # "b" -"abc"[-1] # "c" - -("zero", "one", "two")[0] # "zero" -("zero", "one", "two")[1] # "one" -("zero", "one", "two")[-1] # "two" -``` - -An index expression `d[key]` may also be applied to a dictionary `d`, -to obtain the value associated with the specified key. It is an error -if the dictionary contains no such key. - -An index expression appearing on the left side of an assignment causes -the specified list or dictionary element to be updated: - -```python -a = range(3) # a == [0, 1, 2] -a[2] = 7 # a == [0, 1, 7] - -coins["suzie b"] = 100 -``` - -It is a dynamic error to attempt to update an element of an immutable -type, such as a tuple or string, or a frozen value of a mutable type. - -### Slice expressions - -A slice expression `a[start:stop:stride]` yields a new value containing a -subsequence of `a`, which must be a string, tuple, or list. - -```text -SliceSuffix = '[' [Expression] [':' Test [':' Test]] ']' . -``` - -Each of the `start`, `stop`, and `stride` operands is optional; -if present, and not `None`, each must be an integer. -The `stride` value defaults to 1. -If the stride is not specified, the colon preceding it may be omitted too. -It is an error to specify a stride of zero. - -Conceptually, these operands specify a sequence of values `i` starting -at `start` and successively adding `stride` until `i` reaches or -passes `stop`. The result consists of the concatenation of values of -`a[i]` for which `i` is valid.` - -The effective start and stop indices are computed from the three -operands as follows. Let `n` be the length of the sequence. - -<b>If the stride is positive:</b> -If the `start` operand was omitted, it defaults to -infinity. -If the `end` operand was omitted, it defaults to +infinity. -For either operand, if a negative value was supplied, `n` is added to it. -The `start` and `end` values are then "clamped" to the -nearest value in the range 0 to `n`, inclusive. - -<b>If the stride is negative:</b> -If the `start` operand was omitted, it defaults to +infinity. -If the `end` operand was omitted, it defaults to -infinity. -For either operand, if a negative value was supplied, `n` is added to it. -The `start` and `end` values are then "clamped" to the -nearest value in the range -1 to `n`-1, inclusive. - -```python -"abc"[1:] # "bc" (remove first element) -"abc"[:-1] # "ab" (remove last element) -"abc"[1:-1] # "b" (remove first and last element) -"banana"[1::2] # "aaa" (select alternate elements starting at index 1) -"banana"[4::-2] # "nnb" (select alternate elements in reverse, starting at index 4) -``` - -Unlike Python, Skylark does not allow a slice expression on the left -side of an assignment. - -Slicing a tuple or string may be more efficient than slicing a list -because tuples and strings are immutable, so the result of the -operation can share the underlying representation of the original -operand (when the stride is 1). By contrast, slicing a list requires -the creation of a new list and copying of the necessary elements. - - -## Statements - -```text -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. - -```text -PassStmt = 'pass' . -``` - -Example: - -```python -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. - -```text -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, to update the field of an object: - -```python -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. - -```python -a, b = 2, 3 -(x, y) = f() -[zero, one, two] = range(3) - -[(a, b), (c, d)] = ("ab", "cd") -``` - -The same process for assigning a value to a target expression is used -in `for` loops and in comprehensions. - - -### 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`. - -```text -AssignStmt = Expression ('=' | '+=' | '-=' | '*=' | '/=' | '//=' | '%=') Expression . -``` - -The left-hand side must be a simple target: -a name, an index expression, or a dot expression. - -```python -x -= 1 -x.filename += ".sky" -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: - -```python -x = x - 1 -x.filename = x.filename + ".sky" -``` - -and the third assignment is similar in effect to the following two -statements but does not declare a new temporary variable `i`: - -```python -i = index() -a[i] = a[i] * 2 -``` - -### Function definitions - -A `def` statement creates a named function and assigns it to a variable. - -```text -DefStmt = 'def' identifier '(' [Parameters [',']] ')' ':' Suite . -``` - -Example: - -```python -def twice(x): - return x * 2 - -str(twice) # "<function f>" -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 -four 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. - -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. - -Here are some example parameter lists: - -```python -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 -``` - -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. - -<!-- this is too implementation-oriented; it's not a spec. --> - - -### Return statements - -A `return` statement ends the execution of a function and returns a -value to the caller of the function. - -```text -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. - -```python -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. - -```text -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. - -```python -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. - -```text -IfStmt = 'if' Test ':' Suite {'elif' Test ':' Suite} ['else' ':' Suite] . -``` - -Example: - -```python -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. - -```python -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`: - -```python -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. - -### 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_. - -```text -ForStmt = 'for' LoopVariables 'in' Expression ':' Suite . -``` - -Example: - -```python -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): - -```python -for a, i in [["a", 1], ["b", 2], ["c", 3]]: - print(a, i) # prints "a 1", "b 2", "c 3" -``` - -Because Skylark 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 Skylark, a `for` loop is permitted only within a function definition. -A `for` loop at top level results in a static error. - - -### 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. - -```text -BreakStmt = 'break' . -ContinueStmt = 'continue' . -``` - -Example: - -```python -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 Skylark module, extracts one or -more values from it, and binds them to names in the current module. - -<!-- -The awkwardness of load statements is a consequence of staying a -strict subset of Python syntax, which allows reuse of existing tools -such as editor support. Python import statements are inadequate for -Skylark because they don't allow arbitrary file names for module names. ---> - -Syntactically, a load statement looks like a function call `load(...)`. - -```text -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 -Skylark 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 Skylark 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. - -```python -load("module.sky", "x", "y", "z") # assigns x, y, and z -load("module.sky", "x", y2="y", "z") # assigns x, y2, and z -``` - -A load statement within a function is a static error. - - -## Module execution - -Each Skylark file defines a _module_, which is a mapping from the -names of global variables to their values. -When a Skylark file is executed, whether directly by the application -or indirectly through a `load` statement, a new Skylark thread is -created, and this thread executes all the top-level statements in the -file. -Because if-statements and for-loops cannot appear outside of a function, -control flows from top to bottom. - -If execution reaches the end of the file, module initialization is -successful. -At that point, the value of each of the module's global variables is -frozen, rendering subsequent mutation impossible. -The module is then ready for use by another Skylark thread, such as -one executing a load statement. -Such threads may access values or call functions defined in the loaded -module. - -A Skylark thread may carry state on behalf of the application into -which it is embedded, and application-defined functions may behave -differently depending on this thread state. -Because module initialization always occurs in a new thread, thread -state is never carried from a higher-level module into a lower-level -one. -The initialization behavior of a module is thus independent of -whichever module triggered its initialization. - -If a Skylark thread encounters an error, execution stops and the error -is reported to the application, along with a backtrace showing the -stack of active function calls at the time of the error. -If an error occurs during initialization of a Skylark module, any -active `load` statements waiting for initialization of the module also -fail. - -Skylark provides no mechanism by which errors can be handled within -the language. - - -## Built-in constants and functions - -The outermost block of the Skylark environment is known as the "universe" block. -It defines a number of fundamental values and functions needed by all Skylark programs, -such as `None`, `True`, `False`, and `len`. - -These names are not reserved words so Skylark programs are free to -redefine them in a smaller block such as a function body or even at -the top level of a module. However, doing so may be confusing to the -reader. Nonetheless, this rule permits names to be added to the -universe block in later versions of the language without breaking -existing programs. - - -### None - -`None` is the distinguished value of the type `NoneType`. - -### True and False - -`True` and `False` are the two values of type `bool`. - -### any - -`any(x)` returns `True` if any element of the iterable sequence x is true. -If the iterable is empty, it returns `False`. - -### all - -`all(x)` returns `False` if any element of the iterable sequence x is false. -If the iterable is empty, it returns `True`. - -### bool - -`bool(x)` interprets `x` as a Boolean value---`True` or `False`. -With no argument, `bool()` returns `False`. - -### dict - -`dict` creates a dictionary. It accepts up to one positional -argument, which is interpreted as an iterable of two-element -sequences (pairs), each specifying a key/value pair in -the resulting dictionary. - -`dict` also accepts any number of keyword arguments, each of which -specifies a key/value pair in the resulting dictionary; -each keyword is treated as a string. - -```python -dict() # {}, empty dictionary -dict([(1, 2), (3, 4)]) # {1: 2, 3: 4} -dict([(1, 2), ["a", "b"]]) # {1: 2, "a": "b"} -dict(one=1, two=2) # {"one": 1, "two", 1} -dict([(1, 2)], x=3) # {1: 2, "x": 3} -``` - -With no arguments, `dict()` returns a new empty dictionary. - -`dict(x)` where x is a dictionary returns a new copy of x. - -### dir - -`dir(x)` returns a list of the names of the attributes (fields and methods) of its operand. -The attributes of a value `x` are the names `f` such that `x.f` is a valid expression. - -For example, - -```python -dir("hello") # ['capitalize', 'count', ...], the methods of a string -``` - -Several types known to the interpreter, such as list, string, and dict, have methods, but none have fields. -However, an application may define types with fields that may be read or set by statements such as these: - -```text -y = x.f -x.f = y -``` - -### enumerate - -`enumerate(x)` returns a list of (index, value) pairs, each containing -successive values of the iterable sequence xand the index of the value -within the sequence. - -The optional second parameter, `start`, specifies an integer value to -add to each index. - -```python -enumerate(["zero", "one", "two"]) # [(0, "zero"), (1, "one"), (2, "two")] -enumerate(["one", "two"], 1) # [(1, "one"), (2, "two")] -``` - -### getattr - -`getattr(x, name)` returns the value of the attribute (field or method) of x named `name`. -It is a dynamic error if x has no such attribute. - -`getattr(x, "f")` is equivalent to `x.f`. - -```python -getattr("banana", "split")("a") # ["b", "n", "n", ""], equivalent to "banana".split("a") -``` - -### hasattr - -`hasattr(x, name)` reports whether x has an attribute (field or method) named `name`. - -### hash - -`hash(x)` returns an integer hash value for a string x such that `x == y` -implies `hash(x) == hash(y)`. - -### int - -`int(x[, base])` interprets its argument as an integer. - -If `x` is an `int`, the result is `x`. -If `x` is a `bool`, the result is 0 for `False` or 1 for `True`. - -If `x` is a string, it is interpreted as a sequence of digits in the specified -base, decimal by default. If `base` is zero, `x` is interpreted like an integer -literal, the base being inferred from an optional base marker such as `0b`, -`0o`, or `0x` preceding the first digit. These markers may also be used if -`base` is the corresponding base. Irrespective of base, the string may start -with an optional `+` or `-` sign indicating the sign of the result. - -```python -int("21") # 21 -int("1234", 16) # 4660 -int("0x1234", 16) # 4660 -int("0x1234", 0) # 4660 -int("0x1234") # error (invalid base 10 number) -``` - -### len - -`len(x)` returns the number of elements in its argument. - -It is a dynamic error if its argument is not a sequence. - -### list - -`list` constructs a list. - -`list(x)` returns a new list containing the elements of the -iterable sequence x. - -With no argument, `list()` returns a new empty list. - -### max - -`max(x)` returns the greatest element in the iterable sequence x. - -It is an error if any element does not support ordered comparison, -or if the sequence is empty. - -The optional named parameter `key` specifies a function to be applied -to each element prior to comparison. - -```python -max([3, 1, 4, 1, 5, 9]) # 9 -max("two", "three", "four") # "two", the lexicographically greatest -max("two", "three", "four", key=len) # "three", the longest -``` - -### min - -`min(x)` returns the least element in the iterable sequence x. - -It is an error if any element does not support ordered comparison, -or if the sequence is empty. - -```python -min([3, 1, 4, 1, 5, 9]) # 1 -min("two", "three", "four") # "four", the lexicographically least -min("two", "three", "four", key=len) # "two", the shortest -``` - -### print - -`print(*args, **kwargs)` prints its arguments, followed by a newline. -Arguments are formatted as if by `str(x)` and separated with a space. -Keyword arguments are preceded by their name. - -Example: - -```python -print(1, "hi", x=3) # "1 hi x=3\n" -``` - -Typically the formatted string is printed to the standard error file, -but the exact behavior is a property of the Skylark thread and is -determined by the host application. - -### range - -`range` returns an immutable sequence of integers defined by the specified interval and stride. - -```python -range(stop) # equivalent to range(0, stop) -range(start, stop) # equivalent to range(start, stop, 1) -range(start, stop, step) -``` - -`range` requires between one and three integer arguments. -With one argument, `range(stop)` returns the ascending sequence of non-negative integers less than `stop`. -With two arguments, `range(start, stop)` returns only integers not less than `start`. - -With three arguments, `range(start, stop, step)` returns integers -formed by successively adding `step` to `start` until the value meets or passes `stop`. -A call to `range` fails if the value of `step` is zero. - -A call to `range` does not materialize the entire sequence, but -returns a fixed-size value of type `"range"` that represents the -parameters that define the sequence. -The `range` value is iterable and may be indexed efficiently. - -```python -list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -list(range(3, 10)) # [3, 4, 5, 6, 7, 8, 9] -list(range(3, 10, 2)) # [3, 5, 7, 9] -list(range(10, 3, -2)) # [10, 8, 6, 4] -``` - -The `len` function applied to a `range` value returns its length. -The truth value of a `range` value is `True` if its length is non-zero. - -Range values are comparable: two `range` values compare equal if they -denote the same sequence of integers, even if they were created using -different parameters. - -Range values are not hashable. <!-- should they be? --> - -The `str` function applied to a `range` value yields a string of the -form `range(10)`, `range(1, 10)`, or `range(1, 10, 2)`. - -The `x in y` operator, where `y` is a range, reports whether `x` is equal to -some member of the sequence `y`; the operation fails unless `x` is a -number. - -### repr - -`repr(x)` formats its argument as a string. - -All strings in the result are double-quoted. - -```python -repr(1) # '1' -repr("x") # '"x"' -repr([1, "x"]) # '[1, "x"]' -``` - -### reversed - -`reversed(x)` returns a new list containing the elements of the iterable sequence x in reverse order. - -```python -reversed(range(5)) # [4, 3, 2, 1, 0] -reversed({"one": 1, "two": 2}.keys()) # ["two", "one"] -``` - -### sorted - -`sorted(x)` returns a new list containing the elements of the iterable sequence x, -in sorted order. The sort algorithm is stable. - -```python -sorted([3, 1, 4, 1, 5, 9]) # [1, 1, 3, 4, 5, 9] - -sorted(["two", "three", "four"]) # ["three", "two", "four"] -``` - -### str - -`str(x)` formats its argument as a string. - -If x is a string, the result is x (without quotation). -All other strings, such as elements of a list of strings, are double-quoted. - -```python -str(1) # '1' -str("x") # 'x' -str([1, "x"]) # '[1, "x"]' -``` - -### tuple - -`tuple(x)` returns a tuple containing the elements of the iterable x. - -With no arguments, `tuple()` returns the empty tuple. - -### type - -type(x) returns a string describing the type of its operand. - -```python -type(None) # "NoneType" -type(0) # "int" -``` - -### zip - -`zip()` returns a new list of n-tuples formed from corresponding -elements of each of the n iterable sequences provided as arguments to -`zip`. That is, the first tuple contains the first element of each of -the sequences, the second element contains the second element of each -of the sequences, and so on. The result list is only as long as the -shortest of the input sequences. - -```python -zip() # [] -zip(range(5)) # [(0,), (1,), (2,), (3,), (4,)] -zip(range(5), "abc") # [(0, "a"), (1, "b"), (2, "c")] -``` - -## Built-in methods - -This section lists the methods of built-in types. Methods are selected -using [dot expressions](#dot-expressions). -For example, strings have a `count` method that counts -occurrences of a substring; `"banana".count("a")` yields `3`. - -As with built-in functions, built-in methods accept only positional -arguments except where noted. -The parameter names serve merely as documentation. - - -<a id='dict·get'></a> -### dict·get - -`D.get(key[, default])` returns the dictionary value corresponding to the given key. -If the dictionary contains no such value, `get` returns `None`, or the -value of the optional `default` parameter if present. - -`get` fails if `key` is unhashable, or the dictionary is frozen or has active iterators. - -```python -x = {"one": 1, "two": 2} -x.get("one") # 1 -x.get("three") # None -x.get("three", 0) # 0 -``` - -<a id='dict·items'></a> -### dict·items - -`D.items()` returns a new list of key/value pairs, one per element in -dictionary D, in the same order as they would be returned by a `for` loop. - -```python -x = {"one": 1, "two": 2} -x.items() # [("one", 1), ("two", 2)] -``` - -<a id='dict·keys'></a> -### dict·keys - -`D.keys()` returns a new list containing the keys of dictionary D, in the -same order as they would be returned by a `for` loop. - -```python -x = {"one": 1, "two": 2} -x.keys() # ["one", "two"] -``` - -<a id='dict·pop'></a> -### dict·pop - -`D.pop(key[, default])` returns the value corresponding to the specified -key, and removes it from the dictionary. If the dictionary contains no -such value, and the optional `default` parameter is present, `pop` -returns that value; otherwise, it fails. - -`pop` fails if `key` is unhashable, or the dictionary is frozen or has active iterators. - -```python -x = {"one": 1, "two": 2} -x.pop("one") # 1 -x # {"two": 2} -x.pop("three", 0) # 0 -x.pop("four") # error: missing key -``` - -<a id='dict·popitem'></a> -### dict·popitem - -`D.popitem()` returns the first key/value pair, removing it from the dictionary. - -`popitem` fails if the dictionary is empty, frozen, or has active iterators. - -```python -x = {"one": 1, "two": 2} -x.popitem() # ("one", 1) -x.popitem() # ("two", 2) -x.popitem() # error: empty dict -``` - -<a id='dict·setdefault'></a> -### dict·setdefault - -`D.setdefault(key[, default])` returns the dictionary value corresponding to the given key. -If the dictionary contains no such value, `setdefault`, like `get`, -returns `None` or the value of the optional `default` parameter if -present; `setdefault` additionally inserts the new key/value entry into the dictionary. - -`setdefault` fails if the key is unhashable, or if the dictionary is frozen or has active iterators. - -```python -x = {"one": 1, "two": 2} -x.setdefault("one") # 1 -x.setdefault("three", 0) # 0 -x # {"one": 1, "two": 2, "three": 0} -x.setdefault("four") # None -x # {"one": 1, "two": 2, "three": None} -``` - -<a id='dict·update'></a> -### dict·update - -`D.update([pairs][, name=value[, ...])` makes a sequence of key/value -insertions into dictionary D, then returns `None.` - -If the positional argument `pairs` is present, it must be `None`, -another `dict`, or some other iterable. -If it is another `dict`, then its key/value pairs are inserted into D. -If it is an iterable, it must provide a sequence of pairs (or other iterables of length 2), -each of which is treated as a key/value pair to be inserted into D. - -For each `name=value` argument present, the name is converted to a -string and used as the key for an insertion into D, with its corresponding -value being `value`. - -`update` fails if the dictionary is frozen or has active iterators. - -```python -x = {} -x.update([("a", 1), ("b", 2)], c=3) -x.update({"d": 4}) -x.update(e=5) -x # {"a": 1, "b": "2", "c": 3, "d": 4, "e": 5} -``` - -<a id='dict·values'></a> -### dict·values - -`D.values()` returns a new list containing the dictionary's values, in the -same order as they would be returned by a `for` loop over the -dictionary. - -```python -x = {"one": 1, "two": 2} -x.values() # [1, 2] -``` - -<a id='list·append'></a> -### list·append - -`L.append(x)` appends `x` to the list L, and returns `None`. - -`append` fails if the list is frozen or has active iterators. - -```python -x = [] -x.append(1) # None -x.append(2) # None -x.append(3) # None -x # [1, 2, 3] -``` - -<a id='list·clear'></a> -### list·clear - -`L.clear()` removes all the elements of the list L and returns `None`. -It fails if the list is frozen or if there are active iterators. - -```python -x = [1, 2, 3] -x.clear() # None -x # [] -``` - -<a id='list·extend'></a> -### list·extend - -`L.extend(x)` appends the elements of `x`, which must be iterable, to -the list L, and returns `None`. - -`extend` fails if `x` is not iterable, or if the list L is frozen or has active iterators. - -```python -x = [] -x.extend([1, 2, 3]) # None -x.extend(["foo"]) # None -x # [1, 2, 3, "foo"] -``` - -<a id='list·index'></a> -### list·index - -`L.insert(x[, start[, end]])` finds `x` within the list L and returns its index. - -The optional `start` and `end` parameters restrict the portion of -list L that is inspected. If provided and not `None`, they must be list -indices of type `int`. If an index is negative, `len(L)` is effectively -added to it, then if the index is outside the range `[0:len(L)]`, the -nearest value within that range is used; see [Indexing](#indexing). - -`insert` fails if `x` is not found in L, or if `start` or `end` -is not a valid index (`int` or `None`). - -```python -x = ["b", "a", "n", "a", "n", "a"] -x.index("a") # 1 (bAnana) -x.index("a", 2) # 3 (banAna) -x.index("a", -2) # 5 (bananA) -``` - -<a id='list·insert'></a> -### list·insert - -`L.insert(i, x)` inserts the value `x` in the list L at index `i`, moving -higher-numbered elements along by one. It returns `None`. - -As usual, the index `i` must be an `int`. If its value is negative, -the length of the list is added, then its value is clamped to the -nearest value in the range `[0:len(L)]` to yield the effective index. - -`insert` fails if the list is frozen or has active iterators. - -```python -x = ["b", "c", "e"] -x.insert(0, "a") # None -x.insert(-1, "d") # None -x # ["a", "b", "c", "d", "e"] -``` - -<a id='list·pop'></a> -### list·pop - -`L.pop([index])` removes and returns the last element of the list L, or, -if the optional index is provided, at that index. - -`insert` fails if the index is negative or not less than the length of -the list, of if the list is frozen or has active iterators. - -```python -x = [1, 2, 3] -x.pop() # 3 -x.pop() # 2 -x # [1] -``` - -<a id='list·remove'></a> -### list·remove - -`L.remove(x)` removes the first occurrence of the value `x` from the list L, and returns `None`. - -`remove` fails if the list does not contain `x`, is frozen, or has active iterators. - -```python -x = [1, 2, 3, 2] -x.remove(2) # None (x == [1, 3, 2]) -x.remove(2) # None (x == [1, 3]) -x.remove(2) # error: element not found -``` - -<a id='string·capitalize'></a> -### string·capitalize - -`S.capitalize()` returns a copy of string S with all Unicode letters -that begin words changed to their title case. - -```python -"hello, world!".capitalize() # "Hello, World!" -``` - -<a id='string·count'></a> -### string·count - -`S.count(sub[, start[, end]])` returns the number of occcurences of -`sub` within the string S, or, if the optional substring indices -`start` and `end` are provided, within the designated substring of S. -They are interpreted according to Skylark's [indexing conventions](#indexing). - -```python -"hello, world!".count("o") # 2 -"hello, world!".count("o", 7, 12) # 1 (in "world") -``` - -<a id='string·endswith'></a> -### string·endswith - -`S.endswith(suffix)` reports whether the string S has the specified suffix. - -```python -"filename.sky".endswith(".sky") # True -``` - -<a id='string·find'></a> -### string·find - -`S.find(sub[, start[, end]])` returns the index of the first -occurrence of the substring `sub` within S. - -If either or both of `start` or `end` are specified, -they specify a subrange of S to which the search should be restricted. -They are interpreted according to Skylark's [indexing conventions](#indexing). - -If no occurrence is found, `found` returns -1. - -```python -"bonbon".find("on") # 1 -"bonbon".find("on", 2) # 4 -"bonbon".find("on", 2, 5) # -1 -``` - -<a id='string·format'></a> -### string·format - -`S.format(*args, **kwargs)` returns a version of the format string S -in which bracketed portions `{...}` are replaced -by arguments from `args` and `kwargs`. - -Within the format string, a pair of braces `{{` or `}}` is treated as -a literal open or close brace. -Each unpaired open brace must be matched by a close brace `}`. -The optional text between corresponding open and close braces -specifies which argument to use. - -```text -{} -{field} -``` - -The *field name* may be either a decimal number or a keyword. -A number is interpreted as the index of a positional argument; -a keyword specifies the value of a keyword argument. -If all the numeric field names form the sequence 0, 1, 2, and so on, -they may be omitted and those values will be implied; however, -the explicit and implicit forms may not be mixed. - -```python -"a{x}b{y}c{}".format(1, x=2, y=3) # "a2b3c1" -"a{}b{}c".format(1, 2) # "a1b2c" -"({1}, {0})".format("zero", "one") # "(one, zero)" -``` - -<a id='string·index'></a> -### string·index - -`S.index(sub[, start[, end]])` returns the index of the first -occurrence of the substring `sub` within S, like `S.find`, except -that if the substring is not found, the operation fails. - -```python -"bonbon".index("on") # 1 -"bonbon".index("on", 2) # 4 -"bonbon".index("on", 2, 5) # error: substring not found (in "nbo") -``` - -<a id='string·isalnum'></a> -### string·isalnum - -`S.isalnum()` reports whether the string S is non-empty and consists only -Unicode letters and digits. - -```python -"base64".isalnum() # True -"Catch-22".isalnum() # False -``` - -<a id='string·isalpha'></a> -### string·isalpha - -`S.isalpha()` reports whether the string S is non-empty and consists only of Unicode letters. - -```python -"ABC".isalpha() # True -"Catch-22".isalpha() # False -"".isalpha() # False -``` - -<a id='string·isdigit'></a> -### string·isdigit - -`S.isdigit()` reports whether the string S is non-empty and consists only of Unicode digits. - -```python -"123".isdigit() # True -"Catch-22".isdigit() # False -"".isdigit() # False -``` - -<a id='string·islower'></a> -### string·islower - -`S.islower()` reports whether the string S contains at least one cased Unicode -letter, and all such letters are lowercase. - -```python -"hello, world".islower() # True -"Catch-22".islower() # False -"123".islower() # False -``` - -<a id='string·isspace'></a> -### string·isspace - -`S.isspace()` reports whether the string S is non-empty and consists only of Unicode spaces. - -```python -" ".isspace() # True -"\r\t\n".isspace() # True -"".isspace() # False -``` - -<a id='string·istitle'></a> -### string·istitle - -`S.istitle()` reports whether the string S contains at least one cased Unicode -letter, and all such letters that begin a word are in title case. - -```python -"Hello, World!".istitle() # True -"Catch-22".istitle() # True -"HAL-9000".istitle() # False -"123".istitle() # False -``` - -<a id='string·isupper'></a> -### string·isupper - -`S.isupper()` reports whether the string S contains at least one cased Unicode -letter, and all such letters are uppercase. - -```python -"HAL-9000".isupper() # True -"Catch-22".isupper() # False -"123".isupper() # False -``` - -<a id='string·join'></a> -### string·join - -`S.join(iterable)` returns the string formed by concatenating each -element of its argument, with a copy of the string S between -successive elements. The argument must be an iterable whose elements -are strings. - -```python -", ".join(["one", "two", "three"]) # "one, two, three" -"a".join("ctmrn") # "catamaran" -``` - -<a id='string·lower'></a> -### string·lower - -`S.lower()` returns a copy of the string S with letters converted to lowercase. - -```python -"Hello, World!".lower() # "hello, world!" -``` - -<a id='string·lstrip'></a> -### string·lstrip - -`S.lstrip()` returns a copy of the string S with leading whitespace removed. - -```python -" hello ".lstrip() # " hello" -``` - -<a id='string·partition'></a> -### string·partition - -`S.partition(x)` splits string S into three parts and returns them as -a tuple: the portion before the first occurrence of string `x`, `x` itself, -and the portion following it. -If S does not contain `x`, `partition` returns `(S, "", "")`. - -`partition` fails if `x` is not a string, or is the empty string. - -```python -"one/two/three".partition("/") # ("one", "/", "two/three") -``` - -<a id='string·replace'></a> -### string·replace - -`S.replace(old, new[, count])` returns a copy of string S with all -occurrences of substring `old` replaced by `new`. If the optional -argument `count`, which must be an `int`, is non-negative, it -specifies a maximum number of occurrences to replace. - -```python -"banana".replace("a", "o") # "bonono" -"banana".replace("a", "o", 2) # "bonona" -``` - -<a id='string·rfind'></a> -### string·rfind - -`S.rfind(sub[, start[, end]])` returns the index of the substring `sub` within -S, like `S.find`, except that `rfind` returns the index of the substring's -_last_ occurrence. - -```python -"bonbon".rfind("on") # 4 -"bonbon".rfind("on", None, 5) # 1 -"bonbon".rfind("on", 2, 5) # -1 -``` - -<a id='string·rindex'></a> -### string·rindex - -`S.rindex(sub[, start[, end]])` returns the index of the substring `sub` within -S, like `S.index`, except that `rindex` returns the index of the substring's -_last_ occurrence. - -```python -"bonbon".rindex("on") # 4 -"bonbon".rindex("on", None, 5) # 1 (in "bonbo") -"bonbon".rindex("on", 2, 5) # error: substring not found (in "nbo") -``` - -<a id='string·rpartition'></a> -### string·rpartition - -`S.rpartition(x)` is like `partition`, but splits `S` at the last occurrence of `x`. - -```python -"one/two/three".partition("/") # ("one/two", "/", "three") -``` - -<a id='string·rsplit'></a> -### string·rsplit - -`S.rsplit([sep[, maxsplit]])` splits a string into substrings like `S.split`, -except that when a maximum number of splits is specified, `rsplit` chooses the -rightmost splits. - -```python -"banana".rsplit("n") # ["ba", "a", "a"] -"banana".rsplit("n", 1) # ["bana", "a"] -"one two three".rsplit(None, 1) # ["one two", "three"] -``` - -<a id='string·rstrip'></a> -### string·rstrip - -`S.rstrip()` returns a copy of the string S with trailing whitespace removed. - -```python -" hello ".rstrip() # "hello " -``` - -<a id='string·split'></a> -### string·split - -`S.split([sep [, maxsplit]])` returns the list of substrings of S, -splitting at occurrences of the delimiter string `sep`. - -Consecutive occurrences of `sep` are considered to delimit empty -strings, so `'food'.split('o')` returns `['f', '', 'd']`. -Splitting an empty string with a specified separator returns `['']`. -If `sep` is the empty string, `split` fails. - -If `sep` is not specified or is `None`, `split` uses a different -algorithm: it removes all leading spaces from S -(or trailing spaces in the case of `rsplit`), -then splits the string around each consecutive non-empty sequence of -Unicode white space characters. - -If S consists only of white space, `split` returns the empty list. - -If `maxsplit` is given and non-negative, it specifies a maximum number of splits. - -```python -"one two three".split() # ["one", "two", "three"] -"one two three".split(" ") # ["one", "two", "", "three"] -"one two three".split(None, 1) # ["one", "two three"] -"banana".split("n") # ["ba", "a", "a"] -"banana".split("n", 1) # ["ba", "ana"] -``` - -<a id='string·splitlines'></a> -### string·splitlines - -`S.splitlines([keepends])` returns a list whose elements are the -successive lines of S, that is, the strings formed by splitting S at -line terminators (currently assumed to be a single newline, `\n`, -regardless of platform). - -The optional argument, `keepends`, is interpreted as a Boolean. -If true, line terminators are preserved in the result, though -the final element does not necessarily end with a line terminator. - -```python -"one\n\ntwo".splitlines() # ["one", "", "two"] -"one\n\ntwo".splitlines(True) # ["one\n", "\n", "two"] -``` - - -<a id='string·startswith'></a> -### string·startswith - -`S.startswith(suffix)` reports whether the string S has the specified prefix. - -```python -"filename.sky".startswith("filename") # True -``` - -<a id='string·strip'></a> -### string·strip - -`S.strip()` returns a copy of the string S with leading and trailing whitespace removed. - -```python -" hello ".strip() # "hello" -``` - -<a id='string·title'></a> -### string·title - -`S.title()` returns a copy of the string S with letters converted to titlecase. - -Letters are converted to uppercase at the start of words, lowercase elsewhere. - -```python -"hElLo, WoRlD!".title() # "Hello, World!" -``` - -<a id='string·upper'></a> -### string·upper - -`S.upper()` returns a copy of the string S with letters converted to uppercase. - -```python -"Hello, World!".upper() # "HELLO, WORLD!" -``` - - -## Grammar reference - -```text -File = {Statement | newline} eof . - -Statement = DefStmt | IfStmt | ForStmt | SimpleStmt . - -DefStmt = 'def' identifier '(' [Parameters [',']] ')' ':' Suite . -# NOTE: trailing comma is not permitted if the last argument is `'*' identifier` or `'**' identifier`. - -Parameters = Parameter {',' Parameter}. - -Parameter = identifier | identifier '=' Test | '*' identifier | '**' identifier . - -IfStmt = 'if' Test ':' Suite {'elif' Test ':' Suite} ['else' ':' Suite] . - -ForStmt = 'for' LoopVariables 'in' Expression ':' Suite . - -Suite = [newline indent {Statement} outdent] | SimpleStmt . - -SimpleStmt = SmallStmt {';' SmallStmt} [';'] '\n' . -# NOTE: '\n' optional at EOF - -SmallStmt = ReturnStmt - | BreakStmt | ContinueStmt | PassStmt - | AssignStmt - | ExprStmt - | LoadStmt - . - -ReturnStmt = 'return' [Expression] . -BreakStmt = 'break' . -ContinueStmt = 'continue' . -PassStmt = 'pass' . -AssignStmt = Expression ('=' | '+=' | '-=' | '*=' | '/=' | '//=' | '%=') Expression . -ExprStmt = Expression . - -LoadStmt = 'load' '(' string {',' [identifier '='] string} [','] ')' . - -Test = IfExpr - | PrimaryExpr - | UnaryExpr - | BinaryExpr - . - -IfExpr = Test 'if' Test 'else' Test . - -PrimaryExpr = Operand - | PrimaryExpr DotSuffix - | PrimaryExpr CallSuffix - | PrimaryExpr SliceSuffix - . - -Operand = identifier - | int | string - | ListExpr | ListComp - | DictExpr | DictComp - | '(' [Expression [',']] ')' - . - -DotSuffix = '.' identifier . -SliceSuffix = '[' [Expression] [':' Test [':' Test]] ']' . -CallSuffix = '(' [Arguments [',']] ')' . -# NOTE: trailing comma is not permitted if the last argument is `'*' Test` or `'**' Test`. - -Arguments = Argument {',' Argument} . -Argument = Test | identifier '=' Test | '*' Test | '**' Test . - -ListExpr = '[' [Expression [',']] ']' . -ListComp = '[' Test {CompClause} ']'. - -DictExpr = '{' [Entries [',']] '}' . -DictComp = '{' Entry {CompClause} '}' . -Entries = Entry {',' Entry} . -Entry = Test ':' Test . - -CompClause = 'for' LoopVariables 'in' Test | 'if' Test . - -UnaryExpr = '-' Test - | 'not' Test - . - -BinaryExpr = Test {Binop Test} . - -Binop = 'or' - | 'and' - | '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in' | 'not' 'in' - | '|' - | '&' - | '-' | '+' - | '*' | '%' | '/' | '//' - . - -Expression = Test {',' Test} . -# NOTE: trailing comma permitted only when within [...] or (...). - -LoopVariables = PrimaryExpr {',' PrimaryExpr} . -``` - -Tokens: - -- spaces: newline, eof, indent, outdent. -- identifier. -- literals: string, int. -- plus all quoted tokens such as '+=', 'return'. - -Notes: - -- Ambiguity is resolved using operator precedence. -- The grammar does not enforce the legal order of params and args, - nor that the first compclause must be a 'for'.