{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Specification"},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"specification","__idx":0},"children":["Specification"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This specification was originally derived from the Starlark language"," ","specification. It was influenced by the Python specification,"," ","Copyright 1990-2017, Python Software Foundation."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An introduction to the language, and pointers to the module and context"," ","references, are on the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl"},"children":["overview"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"lexical-elements","__idx":1},"children":["Lexical elements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL syntax (but not semantics) is a strict subset of Python syntax."," ","Practically it means, tools working with Python AST can be used to work"," ","with TSL files."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A TSL program consists of one or more modules. Each module is defined by a"," ","single UTF-8-encoded text file."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL grammar is introduced gradually throughout this document as shown below,"," ","and a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#grammar-reference"},"children":["complete TSL grammar reference"]}," is provided at the end."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Grammar notation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"- lowercase and 'quoted' items are lexical tokens.\n- Capitalized names denote grammar productions.\n- (...) implies grouping.\n- x | y means either x or y.\n- [x] means x is optional.\n- {x} means x is repeated zero or more times.\n- The end of each declaration is marked with a period.\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The contents of a TSL 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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"File = {Statement | newline} eof .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Comments"]},": A hash character (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["#"]},") appearing outside of a string or bytes"," ","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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Punctuation"]},": The following punctuation characters or sequences of"," ","characters are tokens:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"+    -    *    /    //   %    **\n~    &    |    ^    <<   >>\n.    ,    =    ;    :\n(    )    [    ]    {    }\n<    >    >=   <=   ==   !=\n+=   -=   *=   /=   //=  %=\n&=   |=   ^=   <<=  >>=\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Keywords"]},": The following tokens are keywords and may not be used as"," ","identifiers:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["and"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["as"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["elif"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["else"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["in"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["not"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["or"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pass"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["return"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["assert"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["async"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["await"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["class"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["del"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["from"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["global"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["is"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["load"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nonlocal"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["with"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["yield"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Identifiers"]},": an identifier is a sequence of Unicode letters, decimal"," ","digits, and underscores (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_"]},"), not starting with a digit."," ","Identifiers are used as names for values."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"None    True    len\nx       index   starts_with     arg0\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Literals"]},": literals are tokens that denote specific values. TSL"," ","has integer, floating-point, string, and bytes literals."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"0                               # int\n123                             # decimal int\n0x7f                            # hexadecimal int\n0o755                           # octal int\n\n0.0     0.       .0             # float\n1e10    1e+10    1e-10\n1.1e10  1.1e+10  1.1e-10\n\n\"hello\"      'hello'            # string\n'''hello'''  \"\"\"hello\"\"\"        # triple-quoted string\nr'hello'     r\"hello\"           # raw string literal\n\nb\"hello\"     b'hello'           # bytes\nb'''hello''' b\"\"\"hello\"\"\"       # triple-quoted bytes\nrb'hello'    br\"hello\"          # raw bytes literal\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Integer and floating-point literal tokens are defined by the following grammar:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"int         = decimal_lit | octal_lit | hex_lit | 0 .\ndecimal_lit = ('1' … '9') {decimal_digit} .\noctal_lit   = '0' ('o' | 'O') octal_digit {octal_digit} .\nhex_lit     = '0' ('x' | 'X') hex_digit {hex_digit} .\n\nfloat     = decimals '.' [decimals] [exponent]\n          | decimals exponent\n          | '.' decimals [exponent]\n          .\ndecimals  = decimal_digit {decimal_digit} .\nexponent  = ('e'|'E') ['+'|'-'] decimals .\n\ndecimal_digit = '0' … '9' .\noctal_digit   = '0' … '7' .\nhex_digit     = '0' … '9' | 'A' … 'F' | 'a' … 'f' .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a static error if a floating-point literal denotes a value whose"," ","magnitude is too large to be represented as a finite ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," value."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"string-literals","__idx":2},"children":["String literals"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A TSL string literal denotes a string value."," ","In its simplest form, it consists of the desired text"," ","surrounded by matching single- or double-quotation marks:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"abc\"\n'abc'\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Literal occurrences of the chosen quotation mark character must be"," ","escaped by a preceding backslash. So, if a string contains several"," ","of one kind of quotation mark, it may be convenient to quote the string"," ","using the other kind, as in these examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"'Have you read \"To Kill a Mockingbird?\"'\n\"Yes, it's a classic.\"\n\"Have you read \\\"To Kill a Mockingbird?\\\"\"\n'Yes, it\\'s a classic.'\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"string-escapes","__idx":3},"children":["String escapes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Within a string literal, the backslash character ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\"]}," indicates the"," ","start of an ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["escape sequence"]},", a notation for expressing things that"," ","are impossible or awkward to write directly."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["traditional escape sequences"]}," represent the ASCII control"," ","codes 7-13:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"\\a   \\x07 alert or bell\n\\b   \\x08 backspace\n\\f   \\x0C form feed\n\\n   \\x0A line feed\n\\r   \\x0D carriage return\n\\t   \\x09 horizontal tab\n\\v   \\x0B vertical tab\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["literal backslash"]}," is written using the escape ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\\\"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["escaped newline"]},", that is, a backslash at the end of a line, is ignored,"," ","allowing a long string to be split across multiple lines of the source file."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"abc\\\ndef\"\t\t\t# \"abcdef\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["octal escape"]}," encodes a single string element using its octal value."," ","It consists of a backslash followed by one, two, or three octal digits [0-7]."," ","Similarly, a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["hexadecimal escape"]}," encodes a single string element using its hexadecimal value."," ","It consists of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\x"]}," followed by two hexadecimal digits [0-9a-fA-F]."," ","It is an error if the value of an octal or hexadecimal escape is greater than decimal 127."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"'\\0'\t\t\t# \"\\x00\"  a string containing a single NUL element\n'\\12'\t\t\t# \"\\n\"    octal 12 = decimal 10\n'\\101-\\132'\t\t# \"A-Z\"\n'\\119'\t\t\t# \"\\t9\"   = \"\\11\" + \"9\"\n\n'\\x00'\t\t\t# \"\\x00\"  a string containing a single NUL element\n'\\x0A'\t\t\t# \"\\n\"    hexadecimal A = decimal 10\n\"\\x41-\\x5A\"             # \"A-Z\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Unicode escape"]}," denotes the UTF-K encoding of a single, valid Unicode code point,"," ","where K is the implementation-defined number of bits in each string element"," ","(see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#strings"},"children":["strings"]},")."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\uXXXX"]}," form, with exactly four hexadecimal digits,"," ","denotes a 16-bit code point, and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\UXXXXXXXX"]},","," ","with exactly eight digits, denotes a 32-bit code point."," ","It is an error if the value lies in the surrogate range (U+D800 to U+DFFF)"," ","or is greater than U+10FFFF."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"'\\u0041'\t\t# \"A\", an ASCII letter (U+0041)\n'\\u0414' \t\t# \"Д\", a Cyrillic capital letter (U+0414)\n'\\u754c'                # \"界\", a Chinese character (U+754C)\n'\\U0001F600'            # \"😀\", an Emoji (U+1F600)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len"]}," returns the number of UTF-8 code points in a string."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["String literals may contain any valid Unicode text:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"len(\"A\") \t\t# 1\nlen(\"Д\") \t\t# 1\nlen(\"界\")               # 1\nlen(\"😀\")               # 1\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["String literals can denote any valid Unicode text. When the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["repr"]}," ","function is applied to a string, its result is a valid string literal."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ordinary string literal may not contain an unescaped newline,"," ","but a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["multiline string literal"]}," may spread over multiple source lines."," ","It is denoted using three quotation marks at start and end."," ","Within it, unescaped newlines and quotation marks (or even pairs of"," ","quotation marks) have their literal meaning, but three quotation marks"," ","end the literal. This makes it easy to quote large blocks of text with"," ","few escapes."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"haiku = '''\nYesterday it worked.\nToday it is not working.\nThat's computers. Sigh.\n'''\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Regardless of the platform's convention for text line endings, for"," ","example, a linefeed (\\n) on UNIX, or a carriage return followed by a"," ","linefeed (\\r\\n) on Microsoft Windows, an unescaped line ending in a"," ","multiline string literal always denotes a line feed (\\n)."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL also supports ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["raw string literals"]},", which look like an"," ","ordinary single- or double-quotation preceded by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["r"]},". Within a raw"," ","string literal, there is no special processing of backslash escapes,"," ","other than an escaped quotation mark (which denotes a literal"," ","quotation mark), or an escaped newline (which denotes a backslash"," ","followed by a newline). This form of quotation is typically used when"," ","writing strings that contain many quotation marks or backslashes (such"," ","as regular expressions or shell commands) to reduce the burden of"," ","escaping:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"a\\nb\"\t\t# \"a\\nb\"  = 'a' + '\\n' + 'b'\nr\"a\\nb\"\t\t# \"a\\\\nb\" = 'a' + '\\\\' + 'n' + 'b'\n\"a\\\nb\"\t\t# \"ab\"\nr\"a\\\nb\"\t\t# \"a\\\\\\nb\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is an error for a backslash to appear within a string literal other"," ","than as part of one of the escapes described above."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"bytes-literals","__idx":4},"children":["Bytes literals"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A TSL bytes literal denotes a bytes value,"," ","and looks like a string literal, in any of its various forms"," ","(single-quoted, double-quoted, triple-quoted, raw)"," ","preceded by the letter ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"b\"abc\"       b'abc'\nb\"\"\"abc\"\"\"   b'''abc'''\nbr\"abc\"      br'abc'\nrb\"abc\"      rb'abc'\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A raw bytes literal may be indicated by either a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["br"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rb"]}," prefix."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Non-escaped text within a bytes literal denotes the UTF-8 encoding of that text."," ","Bytes literals support the same escape sequences as text strings,"," ","with the following differences:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Octal and hexadecimal escapes may specify any byte value from"," ","zero (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\000"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\x00"]},") to 255 (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\377"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\xFF"]},")."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A Unicode escape ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\uXXXX"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\UXXXXXXXX"]}," denotes the byte"," ","sequence of the UTF-8 encoding of the specified 16- or 32-bit code point."," ","(As with text strings, the code point value must not lie in the surrogate range.)"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any valid string literal that, with a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b"]}," prefix, is also a"," ","valid bytes literal is equivalent in the sense that"," ","the bytes value is the UTF-8 encoding of the string value."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"special-tokens","__idx":5},"children":["Special tokens"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL is space-sensitive language, and indentation is used to"," ","denote a block of statements."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike Python, indentation can only be composed of space characters (U+0020),"," ","not tabs."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TODO: define indent, outdent, semicolon, newline, eof"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"data-types","__idx":6},"children":["Data types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These are the main data types built in to the interpreter:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"None                         # the type of None\nbool                         # True or False\nint                          # a signed 64-bit integer\nfloat                        # an IEEE 754 double-precision floating-point number\nstr                          # a text string, with Unicode encoded as UTF-8\nbytes                        # a byte string\nlist                         # a sequence of values, usually homogeneous in nature\ntuple                        # an unmodifiable sequence of values\ndict                         # an associative mapping from keys to values\nset                          # a collection of unique values\nfunction                     # a function\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Some functions, such as the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Some operations, like ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bool(x)"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str(x)"]},", can be applied to any TSL"," ","value, while others, like integer indexing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[i]"]},", apply only to certain types."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#value-concepts"},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["value concepts"]}]}," section explains the groupings of types"," ","by the operators they support."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"none","__idx":7},"children":["None"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]}," is equal only to itself. Its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"None\""]},"."," ","The truth value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"booleans","__idx":8},"children":["Booleans"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There are two Boolean values, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["True"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]},", representing the"," ","truth or falsehood of a predicate. The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a Boolean is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"bool\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Boolean values are typically used as conditions in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statements,"," ","although any TSL value may be used in this way."," ","Aside from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]}," itself, the following core language values are"," ","considered false in a condition:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["numerical 0 (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0.0"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-0.0"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["the empty string (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"\""]},") and empty bytes (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b\"\""]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["the empty collections ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[]"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["()"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{}"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["set()"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All other core language values are considered true."," ","Application-defined types determine their own truth value."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any value may be explicitly converted to a Boolean using the built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bool"]}," ","function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["True and False may be converted to the values 1 and 0 using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," function,"," ","but Booleans are not numbers. Testing a non-Boolean value for equality to a"," ","Boolean using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]},", regardless of whether they have the same"," ","truth value."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"A\" if 1 + 1 else \"B\"                   # \"A\"\n\"A\" if 0.0 else \"B\"                     # \"B\"\n\nif not mylist:\n    empty = True\n\n1 == True                               # False\nbool(1) == True                         # True\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"integers","__idx":9},"children":["Integers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The TSL integer type represents integers. Its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"int\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Integers are restricted to signed 64-bit values, with a range of"," ","-2^63 to 2^63-1. Arithmetic that overflows this range is a dynamic error."," ","Integers are totally ordered; comparisons follow mathematical"," ","tradition."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]}," operators perform addition and subtraction, respectively."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]}," operator performs multiplication."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["//"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," operations on integers compute floored division and"," ","remainder of floored division, respectively."," ","If the signs of the operands differ, the sign of the remainder ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x % y"]}," ","matches that of the divisor, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},"."," ","For all finite x and y (y ≠ 0), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(x // y) * y + (x % y) == x"]},"."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/"]}," operator implements floating-point division, and"," ","yields a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," result even when its operands are both of type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Integers, including negative values, may be interpreted as bit vectors."," ","Negative values use two's complement representation."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["&"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["^"]}," operators implement bitwise OR, AND, and XOR,"," ","respectively. The unary ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["~"]}," operator yields the bitwise inversion of its"," ","integer argument. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<<"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">>"]}," operators shift the first argument"," ","to the left or right by the number of bits given by the second argument."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any bool, number, or string may be interpreted as an integer by using"," ","the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," built-in function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An integer used in a Boolean context is considered true if it is"," ","non-zero."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"100 // 5 * 9 + 32               # 212\n3 // 2                          # 1\n111111111 * 111111111           # 12345678987654321\nint(\"0xffff\", 16)               # 65535\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"floating-point-numbers","__idx":10},"children":["Floating-point numbers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The TSL floating-point data type represents an IEEE 754"," ","double-precision floating-point number."," ","Its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"float\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Arithmetic on floats using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["//"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," ","operators follows the IEEE 754 standard."," ","However, computing the division or remainder of division by zero is a dynamic error."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An arithmetic operation applied to a mixture of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," ","operands works as if the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," operand were first converted to a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},". For example, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["3.141 + 1"]}," is equivalent to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["3.141 + float(1)"]},". The implicit conversion fails if the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," value is too"," ","large to be represented as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There are two floating-point division operators:"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x / y "]}," yields the floating-point quotient of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},","," ","whereas ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x // y"]}," yields ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["floor(x / y)"]},", that is, the largest"," ","representable integer value not greater than ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x / y"]},"."," ","Although the resulting number is integral, it is represented as a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," if either operand is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," operation computes the remainder of floored division."," ","As with the corresponding operation on integers,"," ","if the signs of the operands differ, the sign of the remainder ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x % y"]}," ","matches that of the divisor, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All float values are ordered, so they may be compared"," ","using operators such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<"]},", and sorted using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sorted"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["IEEE 754 defines two zero values, +0.0 and -0.0."," ","They compare equal to each other."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["IEEE 754 defines two infinite float values ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+Inf"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-Inf"]},","," ","which represent numbers greater/less than all finite float values."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["IEEE 754 defines many \"not a number\" (NaN) values."," ","They are non-finite, and represent the results of dubious operations"," ","such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Inf / Inf"]},". All NaN values compare equal to each other,"," ","but greater than any non-NaN ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," value."," ","(TSL does not follow the IEEE 754 standard for NaN comparisons,"," ","which requires that all comparisons with NaN are false, except NaN != NaN.)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because all NaN values compare equal, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," is how a value is tested against"," ","one. The Python idiom ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x != x"]}," reports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]}," for a NaN in TSL and so detects"," ","nothing."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Neither an infinity nor a NaN has a literal spelling. Both are constructed by"," ","passing their name to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," built-in function, which accepts ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["inf"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["infinity"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nan"]}," case-insensitively with an optional leading sign. Note"," ","that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]}," spells positive infinity with its sign."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"inf = float(\"inf\")              # also float(\"+Inf\"), float(\"INFINITY\")\nneginf = float(\"-inf\")\nnan = float(\"nan\")\n\nstr(inf)                        # \"+inf\"\nstr(neginf)                     # \"-inf\"\nstr(nan)                        # \"nan\"\n\ninf == inf                      # True\nneginf == neginf                # True\ninf == neginf                   # False (the two infinities are distinct)\ninf == -neginf                  # True\n\nnan == nan                      # True\nnan != nan                      # False (this does not detect a NaN)\nnan > inf                       # True (NaN sorts above every other float)\nint(inf)                        # error: cannot convert float infinity to integer\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Division by zero is an error rather than an infinity; ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Inf / Inf"]}," yields a NaN."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A comparison operation may be applied to a mixture of int and float values."," ","The result of such comparisons is mathematically exact, even if neither operand"," ","can be exactly represented by the type of the other."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"(type(1.0), type(1))            # (\"float\", \"int\")\n1.0 == 1                        # True\n\nbig = (1<<53)+1                 # first int value that is not exactly representable as float\n(big + 0.0) == big              # True (zero addition is a no-op)\n(big + 1.0 - 1.0) == big        # False (addition/subtraction caused rounding down)\n((big + 0.0) - big) == 0.0      # True (both operands subject to rounding down)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any bool, number, or string may be interpreted as a floating-point"," ","number by using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]}," built-in function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A float used in a Boolean context is considered true if it is"," ","non-zero (not equal to 0.0 or -0.0). A NaN value is thus considered true."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"1.23e45 * 1.23e45                               # 1.5129e+90\n1.111111111111111 * 1.111111111111111           # 1.2345679012345676\n3.0 / 2                                         # 1.5\n3 / 2.0                                         # 1.5\nfloat(3) / 2                                    # 1.5\n3.0 // 2.0                                      # 1.0\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"strings","__idx":11},"children":["Strings"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A string is an immutable array of elements that encode Unicode text."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a string is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"str\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL uses UTF-8 encoding internally, so each string element is an 8-bit"," ","byte (code unit). The value of K (bits per string element) is therefore 8."," ","A single Unicode code point may span one to four elements depending on its"," ","value."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len"]}," function returns the number of code points in"," ","a string."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Strings may be concatenated with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," operator."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Strings support ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#subscript-expressions"},"children":["indexing"]}," and"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#slice-expressions"},"children":["slicing"]},". The result of both operations is another string"," ","(with length 1, in the case of indexing). Indexing and slicing operate"," ","per-code-point, not per-byte. Users who need byte-level access should"," ","use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Strings are hashable, and thus may be used as keys in a dictionary."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Strings are totally ordered lexicographically, so strings may be"," ","compared using operators such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Strings are ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["not"]}," iterable, so they cannot be used as the operand of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," ","loop or of methods that expect iterables (such as the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list()"]}," constructor)."," ","One must instead explicitly call a method of a string value to obtain an"," ","iterable view of its elements. Because strings are not iterable, they are not"," ","considered to be subtypes of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Collection"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},". (TSL deviates"," ","from Python here to avoid a common pitfall in which a single string is"," ","mistakenly used where a list of strings was intended, resulting in exploding"," ","the string into its individual characters.)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any value may formatted as a string using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["repr"]}," built-in"," ","functions, the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str % tuple"]}," operator, or the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str.format"]}," method."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A string used in a Boolean context is considered true if it is"," ","non-empty."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Strings have several built-in methods."," ","See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin"},"children":["TSL Built-in API Reference"]}," for the full list."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"bytes","__idx":12},"children":["Bytes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["bytes"]}," is an immutable array of integers in the range 0-255."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a bytes is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"bytes\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike a string, which is intended for text, a bytes may represent binary data,"," ","such as the contents of an arbitrary file, without loss."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len"]}," function returns the number of elements (bytes) in a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Two bytes values may be concatenated with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," operator."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Bytes may be ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#subscript-expressions"},"children":["indexed"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#slice-expressions"},"children":["sliced"]},"."," ","The result of indexing is the value of the byte at the given position, as an"," ","integer. The result of slicing is another bytes object."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The comparison ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x in b"]},", where ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b"]}," is a bytes value and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," is an integer in"," ","the range [0, 255], tests whether ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]},"'s value is an element of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b"]},". If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," is"," ","instead another bytes value, the comparison tests whether ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," is contained as"," ","a consecutive subsequence of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b"]}," (analogous to substrings of a string)."," ","It is an error if ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," is an out-of-range integer, or any other type of value."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Like strings, bytes values are hashable, totally ordered, and considered True"," ","if they are non-empty. They are not iterable, and not a subtype of"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Collection"]}," nor ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin"},"children":["TSL Built-in API Reference"]}," for bytes methods."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"lists","__idx":13},"children":["Lists"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A list is a mutable sequence of values."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a list is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"list\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lists are a subtype of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},". The number of elements may be retrieved with"," ","the built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len()"]}," function. Lists can be ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#subscript-expressions"},"children":["indexed"]}," ","to retrieve a single element, and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#slice-expressions"},"children":["sliced"]}," to produce a new"," ","list. Lists can be iterated over by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," loops."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["List may be constructed using bracketed list notation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[]              # an empty list\n[1]             # a 1-element list\n[1, 2]          # a 2-element list\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lists can also be constructed from any iterable by using the built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]}," ","function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["List elements may be added using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["append"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extend"]}," methods,"," ","removed using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["remove"]}," method, or reordered by assignments such as"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list[i] = list[j]"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The concatenation operation ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x + y"]}," yields a new list containing all"," ","the elements of the two lists x and y."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For most types, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x += y"]}," is equivalent to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x = x + y"]},", except that it"," ","evaluates ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," only once, that is, it allocates a new list to hold"," ","the concatenation of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},"."," ","However, if ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," refers to a list, the statement does not allocate a"," ","new list but instead mutates the original list in place, similar to"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x.extend(y)"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lists are not hashable, so may not be used in the keys of a dictionary."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A list used in a Boolean context is considered true if it is"," ","non-empty."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#comprehensions"},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["list comprehension"]}]}," creates a new list whose elements are the"," ","result of some expression applied to each element of another sequence."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[x*x for x in [1, 2, 3, 4]]      # [1, 4, 9, 16]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin"},"children":["TSL Built-in API Reference"]}," for list methods."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"tuples","__idx":14},"children":["Tuples"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A tuple is an immutable sequence of values."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a tuple is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"tuple\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Like lists, tuple is a subtype of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},", and supports the same ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len()"]},","," ","indexing, slicing, and iteration operations. A slice of a tuple returns"," ","another tuple."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tuples are constructed using parenthesized list notation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"()                      # the empty tuple\n(1,)                    # a 1-tuple\n(1, 2)                  # a 2-tuple (\"pair\")\n(1, 2, 3)               # a 3-tuple\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Observe that for the 1-tuple, the trailing comma is necessary to"," ","distinguish it from the parenthesized expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(1)"]},"."," ","1-tuples are seldom used."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL, unlike Python, does not permit a trailing comma to appear in"," ","an unparenthesized tuple expression:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"for k, v, in dict.items(): pass                 # syntax error at 'in'\n_ = [(v, k) for k, v, in dict.items()]          # syntax error at 'in'\n\nsorted(3, 1, 4, 1,)                             # ok\n[1, 2, 3, ]                                     # ok\n{1: 2, 3:4, }                                   # ok\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tuples can be constructed from any iterable by using the built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]}," ","function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike lists, tuples cannot be modified."," ","However, the mutable elements of a tuple may be modified."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tuples are hashable if their elements are hashable, in which case they"," ","may be used as keys of a dictionary."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tuples may be concatenated using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," operator. Note that it is not legal"," ","to directly concatenate a tuple with a list."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A tuple used in a Boolean context is considered true if it is non-empty."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"dictionaries","__idx":15},"children":["Dictionaries"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dictionary is a mutable mapping from keys to values."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a dictionary is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"dict\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]},", Booleans, numbers, strings, and bytes, and tuples"," ","composed from hashable values. Most mutable values, such as lists,"," ","dictionaries, and sets, are not hashable, unless they are frozen."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Attempting to use a non-hashable value as a key in a dictionary"," ","results in a dynamic error."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#dictionary-expressions"},"children":["dictionary expression"]}," specifies a"," ","dictionary as a set of key/value pairs enclosed in braces:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"coins = {\n  \"penny\": 1,\n  \"nickel\": 5,\n  \"dime\": 10,\n  \"quarter\": 25,\n}\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d[k]"]},", where ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d"]}," is a dictionary and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["k"]}," is a key,"," ","retrieves the value associated with the key. If the dictionary"," ","contains no such item, the operation fails:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"coins[\"penny\"]          # 1\ncoins[\"dime\"]           # 10\ncoins[\"silver dollar\"]  # error: key not found\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A key/value item may be added to a dictionary, or updated if the key"," ","is already present, by using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d[k]"]}," on the left side of an assignment:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"len(coins)\t\t\t\t# 4\ncoins[\"shilling\"] = 20\nlen(coins)\t\t\t\t# 5, item was inserted\ncoins[\"shilling\"] = 5\nlen(coins)\t\t\t\t# 5, existing item was updated\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dictionary can also be constructed using a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#comprehensions"},"children":["dictionary"," ","comprehension"]},", which evaluates a pair of expressions,"," ","the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["key"]}," and the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["value"]},", for every element of another iterable such"," ","as a list. This example builds a mapping from each word to its length:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"words = [\"able\", \"baker\", \"charlie\"]\n{x: len(x) for x in words}\t# {\"charlie\": 7, \"baker\": 5, \"able\": 4}\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Dictionaries are a subtype of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Mapping"]},". ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len()"]}," returns the number of"," ","entries. Iteration yields the keys in the order that they were inserted;"," ","updating the value associated with an existing key does not affect the"," ","iteration order."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x = dict([(\"a\", 1), (\"b\", 2)])          # {\"a\": 1, \"b\": 2}\nx.update([(\"a\", 3), (\"c\", 4)])          # {\"a\": 3, \"b\": 2, \"c\": 4}\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"for name in coins:\n  print(name, coins[name])\t# prints \"quarter 25\", \"dime 10\", ...\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Like all mutable values in TSL, a dictionary can be frozen, and"," ","once frozen, all subsequent operations that attempt to update it will"," ","fail."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dictionary used in a Boolean context is considered true if it is"," ","non-empty."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The binary ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]}," operation may be applied to two dictionaries. It yields a new"," ","dictionary whose set of keys is the union of the sets of keys of the two"," ","operands. The corresponding values are taken from the operands, where the value"," ","taken from the right operand takes precedence if both contain a given key."," ","Iterating over the keys in the resulting dictionary first yields all keys in"," ","the left operand in insertion order, then all keys in the right operand that"," ","were not present in the left operand, again in insertion order."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There is also an augmented assignment version of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]}," operation. For two"," ","dictionaries ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d1"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d2"]},", the expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d1 |= d2"]}," behaves similar to"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d1 = d1 | d2"]},", but mutates ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d1"]}," in-place rather than assigning a new"," ","dictionary to it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Dictionaries may be compared for equality using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["!="]},". 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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin"},"children":["TSL Built-in API Reference"]}," for dictionary methods."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sets","__idx":16},"children":["Sets"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A set is a mutable collection of unique values - the set's ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["elements"]},"."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," of a set is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"set\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sets provide constant-time operations to insert, remove, or check for the"," ","presence of a value. Sets are implemented using a hash table, and therefore,"," ","just like keys of a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#dictionaries"},"children":["dictionary"]},", elements of a set must be"," ","hashable. A value may be used as an element of a set if and only if it may be"," ","used as a key of a dictionary."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sets may be constructed using the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#set"},"children":["set()"]}," built-in function, which returns"," ","a set containing all the elements of its optional argument, which must be an"," ","iterable. Calling ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["set()"]}," without an argument constructs an empty set."," ","Sets have no literal syntax."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["in"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["not in"]}," operations check whether a value is (or is not) in a set:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"s = set([\"a\", \"b\", \"c\"])\n\"a\" in s  # True\n\"z\" in s  # False\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A set is a subtype of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Collection"]},". Its size can be retrieved using the"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#len"},"children":["len()"]}," built-in function, and the order of iteration is the order in"," ","which elements were inserted. (Attempting to add an element that is already"," ","present is a no-op, and does not change the iteration order.)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"s = set([\"z\", \"y\", \"z\", \"y\"])\nlen(s)       # prints 2\ns.add(\"x\")\ns.add(\"z\")\nlen(s)       # prints 3\nfor e in s:\n    print(e)  # prints \"z\", \"y\", \"x\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A set used in Boolean context is true if and only if it is non-empty."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"s = set()\n\"non-empty\" if s else \"empty\"  # \"empty\"\nt = set([\"x\", \"y\"])\n\"non-empty\" if t else \"empty\"  # \"non-empty\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sets may be compared for equality or inequality using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["!="]},". A set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["s"]}," ","is equal to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["t"]}," if and only if ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["t"]}," is a set containing the same elements;"," ","iteration order is not significant. In particular, a set is ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["not"]}," equal to the"," ","list of its elements. Sets are not ordered with respect to other sets, and an"," ","attempt to compare two sets using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<="]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">="]},", or to sort a sequence of"," ","sets, will fail."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"set() == set()              # True\nset() != []                 # True\nset([1, 2]) == set([2, 1])  # True\nset([1, 2]) != [1, 2]       # True\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]}," operation on two sets returns the union of the two sets: a set"," ","containing the elements found in either one or both of the original sets."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"set([1, 2]) | set([3, 2])  # set([1, 2, 3])\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["&"]}," operation on two sets returns the intersection of the two sets: a set"," ","containing only the elements found in both of the original sets."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"set([1, 2]) & set([2, 3])  # set([2])\nset([1, 2]) & set([3, 4])  # set()\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]}," operation on two sets returns the difference of the two sets: a set"," ","containing the elements found in the left-hand side set but not the right-hand"," ","side set."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"set([1, 2]) - set([2, 3])  # set([1])\nset([1, 2]) - set([3, 4])  # set([1, 2])\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["^"]}," operation on two sets returns the symmetric difference of the two sets:"," ","a set containing the elements found in exactly one of the two original sets, but"," ","not in both."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"set([1, 2]) ^ set([2, 3])  # set([1, 3])\nset([1, 2]) ^ set([3, 4])  # set([1, 2, 3, 4])\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In each of the above operations, the elements of the resulting set retain their"," ","order from the two operand sets, with all elements that were drawn from the"," ","left-hand side ordered before any element that was only present in the"," ","right-hand side."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The corresponding augmented assignments, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|="]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["&="]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-="]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["^="]},", modify the"," ","left-hand set in place."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"s = set([1, 2])\ns |= set([2, 3, 4])     # s now equals set([1, 2, 3, 4])\ns &= set([0, 1, 2, 3])  # s now equals set([1, 2, 3])\ns -= set([0, 1])        # s now equals set([2, 3])\ns ^= set([3, 4])        # s now equals set([2, 4])\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Like all mutable values in TSL, a set can be frozen, and once frozen, all"," ","subsequent operations that attempt to update it will fail."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin"},"children":["TSL Built-in API Reference"]}," for set methods."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"functions","__idx":17},"children":["Functions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A function value represents a function defined in TSL."," ","Its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"function\""]},"."," ","A function value used in a Boolean context is always considered true."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Functions defined by a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#function-definitions"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statement"]}," are named;"," ","functions defined by a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#lambda-expressions"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda"]}," expression"]}," are anonymous."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Function definitions may be nested, and an inner function may refer"," ","to a local variable of an outer function."," ","TSL has no equivalent of Python's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nonlocal"]}," keyword,"," ","and thus no way for an inner function to assign to a local"," ","variable of an outer function."," ","However, the inner function may mutate the value of such variables"," ","until they become frozen."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A function definition defines zero or more named parameters."," ","TSL has a rich mechanism for passing arguments to functions."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The example below shows a definition and call of a function of two"," ","required parameters, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def idiv(x, y):\n  return x // y\n\nidiv(6, 3)\t\t# 2\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"idiv(x=6, y=3)\t\t# 2\nidiv(y=3, x=6)\t\t# 2\n\nidiv(6, y=3)\t\t# 2\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Optional parameters:"]}," A parameter declaration may specify a"," ","default value using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name=value"]}," syntax; such a parameter is"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["optional"]},". The default value expression is evaluated during"," ","execution of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(x, y=3):\n  return x, y\n\nf(1, 2)\t# (1, 2)\nf(1)\t# (1, 3)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"# module a.sky\ndef f(x, mutable_var=[]):\n  mutable_var.append(x)\n  return mutable_var\n\nf(4, [1,2,3])           # [1, 2, 3, 4]\nf(1)                    # [1]\nf(2)                    # [1, 2], not [2]!\n\n# module b.sky\nimport a\na.f(3)                  # error: cannot append to frozen list\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Variadic functions:"]}," Some functions allow callers to provide an"," ","arbitrary number of arguments."," ","After all required and optional parameters, a function definition may"," ","specify a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["variadic arguments list"]}," or ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["varargs"]}," parameter, indicated by a"," ","star preceding the parameter name: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]},"."," ","Any surplus positional arguments provided by the caller are formed"," ","into a tuple and assigned to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["args"]}," parameter."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(x, y, *args):\n  return x, y, args\n\nf(1, 2)                 # (1, 2, ())\nf(1, 2, 3, 4)           # (1, 2, (3, 4))\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Keyword-variadic functions:"]}," Some functions allow callers to"," ","provide an arbitrary sequence of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name=value"]}," keyword arguments."," ","A function definition may include a final ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["keyword arguments dictionary"]}," or"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["kwargs"]}," parameter, indicated by a double-star preceding the parameter"," ","name: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["**kwargs"]},"."," ","Any surplus named arguments that do not correspond to named parameters"," ","are collected in a new dictionary and assigned to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["kwargs"]}," parameter:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(x, y, **kwargs):\n  return x, y, kwargs\n\nf(1, 2)                 # (1, 2, {})\nf(x=2, y=1)             # (2, 1, {})\nf(x=2, y=1, z=3)        # (2, 1, {\"z\": 3})\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a static error if any two parameters of a function have the same name."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Just as a function definition may accept an arbitrary number of"," ","positional or named arguments, a function call may provide an"," ","arbitrary number of positional or named arguments supplied by a"," ","list or dictionary:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(a, b, c=5):\n  return a * b + c\n\nf(*[2, 3])              # 11\nf(*[2, 3, 7])           # 13\nf(*[2])                 # error: f takes at least 2 arguments (1 given)\n\nf(**dict(b=3, a=2))             # 11\nf(**dict(c=7, a=2, b=3))        # 13\nf(**dict(a=2))                  # error: f takes at least 2 arguments (1 given)\nf(**dict(d=4))                  # error: f got unexpected keyword argument \"d\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a static error if a function call has two named arguments of the"," ","same name, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f(x=1, x=2)"]},". A call that provides a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["**kwargs"]}," ","argument may yet have two values for the same name, such as"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f(x=1, **dict(x=2))"]},". This results in a dynamic error."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Function arguments are evaluated in the order they appear in the call."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike Python, TSL does not allow more than one ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," argument in a"," ","call, and if a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," argument is present it must appear after all"," ","positional and named arguments. In particular, even though keyword-only"," ","arguments (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#function-definitions"},"children":["see below"]},") are declared after ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," in a"," ","function's definition, they nevertheless must appear before ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," in a call"," ","to the function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A function call completes normally after the execution of either a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]}," if the return statement had no operand or if the"," ","function completed without executing a return statement."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(x):\n  if x == 0:\n    return\n  if x < 0:\n    return -x\n  print(x)\n\nf(1)            # returns None after printing \"1\"\nf(0)            # returns None without printing\nf(-1)           # returns 1 without printing\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"built-in-functions","__idx":18},"children":["Built-in functions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A built-in function is a function or method implemented by the interpreter"," ","or the application into which the interpreter is embedded."," ","Its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#type"},"children":["type"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"builtin_function_or_method\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A built-in function value used in a Boolean context is always considered true."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Many built-in functions are predeclared in the environment;"," ","see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#name-binding-and-variables"},"children":["Name Resolution"]},"."," ","Some built-in functions such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len"]}," are ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["universal"]},", that is,"," ","available to all TSL programs."," ","The host application may predeclare additional built-in functions"," ","in the environment of a specific module."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Except where noted, built-in functions accept only positional arguments."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The predeclared environment includes the constructible exception type"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Exception"]}," and a set of non-constructible exception type identifiers"," ","(",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RuntimeError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ValueError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TypeError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KeyError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IndexError"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LookupError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IOError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ArithmeticError"]},") usable only in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clauses."," ","See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#exception-types"},"children":["Exception types"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"name-binding-and-variables","__idx":19},"children":["Name binding and variables"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After a TSL file is parsed, but before its execution begins, the"," ","TSL interpreter checks statically that the program is well formed."," ","For example, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," statements may appear only within"," ","a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," loop; ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["return"]}," statements may appear only"," ","within a function; and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statements may appear only outside any function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["scope"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Four TSL constructs bind names, as illustrated in the example below:"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statements (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mylib"]},"),"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statements (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["c"]},"),"," ","function parameters (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["d"]},"),"," ","and assignments (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["e"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["h"]},", including the augmented assignment ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["e += h"]},")."," ","Variables may be assigned or re-assigned explicitly (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["e"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["h"]},"), or implicitly, as"," ","in a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f"]},") or comprehension ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clause (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["g"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]},")."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import mylib\n\ndef c(d):\n  e = 0\n  for f in d:\n     print([True for g in f])\n     e += 1\n\nh = [2*i for i in mylib.items]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The environment of a TSL program is structured as a tree of"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["lexical blocks"]},", each of which may contain name bindings."," ","The tree of blocks is parallel to the syntax tree."," ","Blocks are of five kinds."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["At the root of the tree is the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["predeclared"]}," block,"," ","which binds several names implicitly."," ","The set of predeclared names includes the universal"," ","constant values ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["True"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]},", and"," ","various built-in functions such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]},";"," ","these functions are immutable and stateless."," ","An application may pre-declare additional names"," ","to provide domain-specific functions to that file, for example."," ","These additional functions may have side effects on the application."," ","TSL programs cannot change the set of predeclared bindings"," ","or assign new values to them. Unlike standard Starlark, TSL does not"," ","allow shadowing of built-in or application-defined names in any scope."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Nested beneath the predeclared block is the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["module"]}," block,"," ","which contains the bindings of the current module."," ","Bindings in the module block (such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mylib"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["c"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["h"]}," in the"," ","example) are called ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["global"]}," and may be visible to other modules."," ","The module block is 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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Nested beneath the module block is the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["file"]}," block,"," ","which contains bindings local to the current file."," ","Names in this block are bound by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statements."," ","The sets of names bound in the file block and in the module block do not overlap:"," ","it is an error for an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement to bind the name of a global,"," ","or for a top-level statement to bind a name bound by an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A file block contains a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["function"]}," block for each top-level"," ","function, and a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["comprehension"]}," block for each top-level comprehension."," ","Bindings in either of these kinds of block,"," ","and in the file block itself, are called ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["local"]},"."," ","(In the example, the bindings for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["e"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["g"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]}," are all local.)"," ","Additional functions and comprehensions, and their blocks, may be"," ","nested in any order, to any depth."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If name is bound anywhere within a block, all uses of the name within"," ","the block are treated as references to that binding,"," ","even if the use appears before the binding."," ","This is true even at the top level, unlike Python."," ","The binding of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]}," on the last line of the example below makes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]}," ","local to the function ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["hello"]},", so the use of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]}," in the print"," ","statement also refers to the local ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},", even though it appears"," ","earlier."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"y = \"goodbye\"\n\ndef hello():\n  for x in (1, 2):\n    if x == 2:\n      print(y) # prints \"hello\"\n    if x == 1:\n      y = \"hello\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a dynamic error to evaluate a reference to a local variable"," ","before it has been bound:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f():\n  print(x)              # dynamic error: local variable x referenced before assignment\n  x = \"hello\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The same is true for global variables:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"print(x)                # dynamic error: global variable x referenced before assignment\nx = \"hello\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The same is also true for nested loops in comprehensions."," ","In the (unnatural) examples below, the scope of the variables ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]},","," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["z"]}," is the entire compehension block, except the operand of the first"," ","loop (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[]"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[1]"]},"), which is resolved in the enclosing environment."," ","The second loop may thus refer to variables defined by the third (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["z"]},"),"," ","even though such references would fail if actually executed."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"[1//0 for x in [] for y in z for z in ()]   # []   (no error)\n[1//0 for x in [1] for y in z for z in ()]  # dynamic error: local variable z referenced before assignment\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a static error to refer to a name that has no binding at all."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"def f():\n  if False:\n    g()                   # static error: undefined: g\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["(This behavior differs from Python, which treats such references as global,"," ","and thus does not report an error until the expression is evaluated.)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Global variables may be re-assigned during module evaluation."," ","This includes augmented assignments such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x += 1"]},"."," ","Exported global variables are still frozen after module execution completes."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x = 1\nx = 2                   # valid in TSL: globals can be re-assigned\nx += 3                  # valid in TSL: augmented assignment on globals is allowed\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["However, it is not permitted to shadow built-in or application-defined names"," ","in any scope, nor to reassign imported module names:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import fs\nfs = 1234               # static error: cannot reassign imported module name\nprint = 1234            # static error: cannot shadow built-in name\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Internally, TSL uses ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["cell variables"]}," to support closures: when a local"," ","variable in an outer function is referenced by an inner function, the"," ","variable is converted to a cell that can be shared across scopes."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A name appearing after a dot, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["split"]}," in"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["get_filename().split('/')"]},", is not resolved statically."," ","The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#dot-expressions"},"children":["dot expression"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".split"]}," is a dynamic operation"," ","on the value returned by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["get_filename()"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"value-concepts","__idx":20},"children":["Value concepts"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL has over a dozen core ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#data-types"},"children":["data types"]},"."," ","An application that embeds the TSL intepreter may"," ","define additional types that behave like TSL values."," ","All values, whether core or"," ","application-defined, implement a few basic behaviors:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"str(x)\t\t-- return a string representation of x\ntype(x)\t\t-- return a string describing the type of x\nbool(x)\t\t-- convert x to a Boolean truth value\nhash(x)\t\t-- return a hash code for x\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"identity-and-mutation","__idx":21},"children":["Identity and mutation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL 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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["print"]}," change"," ","the state of the application that embeds the interpreter."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Values of some data types, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bool"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},", are ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["immutable"]},"; they can never change."," ","Immutable values have no notion of ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["identity"]},": it is impossible for a"," ","TSL program to tell whether two integers, for instance, are"," ","represented by the same object; it can tell only whether they are"," ","equal."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Values of other data types, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]},", are"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["mutable"]},": they may be modified by a statement such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[i] = 0"]}," or"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["items.clear()"]},". Although ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["function"]}," values are not"," ","directly mutable, they may refer to mutable values indirectly, so for"," ","this reason we consider them mutable too. TSL values of these"," ","types are actually ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["references"]}," to variables."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Copying a reference to a variable, using an assignment statement for"," ","instance, creates an ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["alias"]}," for the variable, and the effects of"," ","operations applied to the variable through one alias are visible"," ","through all others."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x = []                          # x refers to a new empty list variable\ny = x                           # y becomes an alias for x\nx.append(1)                     # changes the variable referred to by x\nprint(y)                        # \"[1]\"; y observes the mutation\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL uses ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(y):\n    y.append(1)                 # changes the variable referred to by x\n\nx = []                          # x refers to a new empty list variable\nf(x)                            # f's parameter y becomes an alias for x\nprint(x)                        # \"[1]\"; x observes the mutation\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["As in all imperative languages, understanding ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["aliasing"]},", the"," ","relationship between reference values and the variables to which they"," ","refer, is crucial to writing correct programs."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"freezing-a-value","__idx":22},"children":["Freezing a value"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL has a feature unusual among imperative programming languages:"," ","a mutable value may be ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["frozen"]}," so that all subsequent attempts to"," ","mutate it fail with a dynamic error; the value, and all other values"," ","reachable from it, become ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["immutable"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Immediately after execution of a TSL module, all values in its"," ","top-level environment are frozen. Because all the global variables of"," ","an initialized TSL 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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Some values may be \"unfreezeable\" and therefore not available post module"," ","execution. Values that are not able to be frozen are likely holding OS resources"," ","and are designed to be short lived. It's likely that the attributes on that type"," ","are freezeable, and so you'd just need to grab a copy to that type to export"," ","(if needed). See the documentation for types to see if it's unfreezable. The"," ","behavior when trying to freeze an unfreezeable value is up to the runtime"," ","environment, it may cause a failure, or it may silently not export the value."," ","If a container type contains an unfreezeable value, it should be treated as"," ","unfreezeable."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"hashing","__idx":23},"children":["Hashing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]}," data type is implemented using hash tables, so"," ","only ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["hashable"]}," values are suitable as keys of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]},"."," ","Attempting to use a non-hashable value as the key in a dictionary"," ","results in a dynamic error."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The hash of a value is a 64-bit integer chosen so that two equal"," ","values have the same hash, in other words, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x == y => hash(x) == hash(y)"]},"."," ","TSL uses 64-bit FNV-1a (FNV64-1a) as the hash algorithm."," ","A hashable value has the same hash throughout its lifetime."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Values of the types ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bool"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},","," ","which are all immutable, are hashable."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Values of mutable types such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]}," are not"," ","hashable, unless they have become immutable due to ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["freezing"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]}," value is hashable only if all its elements are hashable."," ","Thus ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(\"localhost\", 80)"]}," is hashable but ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["([127, 0, 0, 1], 80)"]}," is not."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Values of the types ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["function"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["builtin_function_or_method"]}," 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 ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#comparisons"},"children":["Comparisons"]},"),"," ","so their hash values are derived from their identity."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"collection-types","__idx":24},"children":["Collection types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL defines several abstract data types. Although these are not directly"," ","referenced by name in TSL programs, they are useful concepts for"," ","understanding what operations a type supports and for documenting what types a"," ","function expects."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Collection"]},": A type that is:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"em","attributes":{},"children":["iterable"]}," (can appear on the right-hand side of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," loop, whether that"," ","be a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement or comprehension ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clause),"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["can have its length taken via the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len()"]}," function, and"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["supports testing for membership via the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["in"]}," operator."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples include ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["set"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]}," (its elements are its"," ","keys), but not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]}," since they are not iterable."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},": A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Collection"]}," that additionally supports subscript and slicing"," ","expressions, following the semantics of indexing. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]}," are"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},"s, but not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["set"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]},"."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Mapping"]},": A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Collection"]}," of keys associated with values, where a value can"," ","be retrieved by subscripting with the key. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]}," is the only example of a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Mapping"]}," in the core language."]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Applications may define additional data structures that may or may not have a"," ","particular abstract data type. If it does have the type, it must support all of"," ","its required operations, but the converse is not necessarily true."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"iteration","__idx":25},"children":["Iteration"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a dynamic error to mutate a collection such as a list, set, or dictionary"," ","while iterating over it. This helps avoid a common source of errors and"," ","implementation pitfalls. For example, the following code snippet is"," ","syntactically valid in both Python and TSL:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"a = [5, 11, 12, 13, 8]\nfor x in a:\n    if x > 10:\n        a.remove(x)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In Python, the result is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[5, 12, 8]"]}," -- the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["12"]}," is missed due to the implicit"," ","iterator's internal integer indexing. "," But in TSL, an error is raised at the attempt to"," ","mutate ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a"]}," inside the loop. The same code would succeed with the correct result"," ","in both languages if the loop were rewritten as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for x in list(a)"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike Python 3, the result of certain methods like ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["enumerate()"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["zip()"]}," is"," ","a new list rather than an iterator object. Loops over such lists will not"," ","prevent modifying the original list."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"indexing","__idx":26},"children":["Indexing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Many TSL operators and functions require an index operand ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]},","," ","such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[i]"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list.insert(i, x)"]},". Others require two indices ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]}," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["j"]}," that indicate the start and end of a subsequence, such as"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[i:j]"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list.index(x, i, j)"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["string.find(x, i, j)"]},"."," ","All such operations follow similar conventions, described here."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Indexing in TSL is ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]}," has index ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n-1"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"hello\"[0]\t\t\t# \"h\"\n\"hello\"[4]\t\t\t# \"o\"\n\"hello\"[5]\t\t\t# error: index out of range\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For subsequence operations that require two indices, the first is"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["inclusive"]}," and the second ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["exclusive"]},". Thus ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[i:j]"]}," indicates the"," ","sequence starting with element ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]}," up to but not including element"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["j"]},". The length of this subsequence is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["j-i"]},". This convention is known"," ","as ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["half-open indexing"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"hello\"[1:4]\t\t\t# \"ell\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"hello\"[1:]                     # \"ello\"\n\"hello\"[:4]                     # \"hell\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"hello\"[-1]                     # \"o\",  like \"hello\"[4]\n\"hello\"[-3:-1]                  # \"ll\", like \"hello\"[2:4]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Second, for subsequence operations, if the value is still negative, it"," ","is replaced by zero, or if it is greater than the length ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]}," of the"," ","sequence, it is replaced by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]},". In effect, the index is \"truncated\" to"," ","the nearest value in the range ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[0:n]"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"hello\"[-1000:1000]\t\t# \"hello\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This truncation step does not apply to indices of individual elements:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"hello\"[-6]\t\t# error: index out of range\n\"hello\"[-5]\t\t# \"h\"\n\"hello\"[4]\t\t# \"o\"\n\"hello\"[5]\t\t# error: index out of range\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"expressions","__idx":27},"children":["Expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An expression specifies the computation of a value."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The TSL grammar defines several categories of expression."," ","An ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["primary"]}," expression."," ","In some places in the TSL grammar where an expression is expected,"," ","it is legal to provide a comma-separated list of expressions denoting"," ","a tuple."," ","The grammar uses ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Expressions"]}," where a multiple-component expression is allowed,"," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Expression"]}," where it accepts an expression of only a single component."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Expressions = Expression {',' Expression} .\n\nExpression = IfExpr | PrimaryExpr | UnaryExpr | BinaryExpr | LambdaExpr .\n\nPrimaryExpr = Operand\n            | PrimaryExpr DotSuffix\n            | PrimaryExpr CallSuffix\n            | PrimaryExpr SubscriptSuffix\n            .\n\nOperand = identifier\n        | int | float | string | bytes\n        | ListExpr | ListComp\n        | DictExpr | DictComp\n        | '(' [Expressions] [,] ')'\n        .\n\nDotSuffix       = '.' identifier .\nCallSuffix      = '(' [Arguments [',']] ')' .\nSubscriptSuffix = '[' [Expressions] [':' Expression [':' Expression]] ']'\n                | '[' Expressions ']'\n                .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"identifiers","__idx":28},"children":["Identifiers"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Operand = identifier\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An identifier is a name that identifies a value."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lookup of locals and globals may fail if not yet defined."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"literals","__idx":29},"children":["Literals"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL supports literals of four different kinds:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Operand = int | float | string | bytes\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Evaluation of an int, float, string, or bytes literal yields the value of that literal."," ","See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#lexical-elements"},"children":["Literals"]}," for details."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"parenthesized-expressions","__idx":30},"children":["Parenthesized expressions"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Operand = '(' [Expressions] ')'\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"1 + 2 * 3 + 4                   # 11\n(1 + 2) * (3 + 4)               # 21\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"()                              # (), the empty tuple\n(1,)                            # (1,), a tuple of length 1\n(1, 2)                          # (1, 2), a 2-tuple or pair\n(1, 2, 3)                       # (1, 2, 3), a 3-tuple or triple\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In some contexts, such as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["return"]}," or assignment statement or the"," ","operand of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement, a tuple may be expressed without"," ","parentheses."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x, y = 1, 2\n\nreturn 1, 2\n\nfor x in 1, 2:\n   print(x)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL (like Python 3) does not accept an unparenthesized tuple"," ","or lambda expression as the operand of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clause in a comprehension:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[2*x for x in 1, 2, 3]\t       \t# parse error: unexpected ','\n[2*x for x in lambda: 0]       \t# parse error: unexpected 'lambda'\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"dictionary-expressions","__idx":31},"children":["Dictionary expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"DictExpr = '{' [Entries [',']] '}' .\nEntries  = Entry {',' Entry} .\nEntry    = Expression ':' Expression .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"{}\n{\"one\": 1}\n{\"one\": 1, \"two\": 2,}\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The key and value expressions are evaluated in left-to-right order."," ","Evaluation fails if the same key is used multiple times."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Only ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#hashing"},"children":["hashable"]}," values may be used as the keys of a dictionary."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"list-expressions","__idx":32},"children":["List expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"ListExpr = '[' [Expressions [',']] ']' .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Element expressions are evaluated in left-to-right order."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[]                      # [], empty list\n[1]                     # [1], a 1-element list\n[1, 2, 3,]              # [1, 2, 3], a 3-element list\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"unary-operators","__idx":33},"children":["Unary operators"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There are four unary operators, all appearing before their operand:"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["~"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["not"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"UnaryExpr = '+' Expression\n          | '-' Expression\n          | '~' Expression\n          | 'not' Expression\n          .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"+ number        unary positive          (int, float)\n- number        unary negation          (int, float)\n~ number        unary bitwise inversion (int)\nnot x           logical negation        (any type)\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]}," operators may be applied to any number:"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," yields the operand unchanged, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]}," yields its negation."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," operator is never necessary in a correct program but may"," ","serve as an assertion that its operand is a number, or as documentation."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"if x > 0:\n\treturn +1\nelif x < 0:\n\treturn -1\nelse:\n\treturn 0\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["not"]}," operator returns the negation of the truth value of its"," ","operand."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"not True                        # False\nnot False                       # True\nnot [1, 2, 3]                   # False\nnot \"\"                          # True\nnot 0                           # True\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["~"]}," operator yields the bitwise inversion of its integer argument."," ","The bitwise inversion of x is defined as -(x+1)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"~1                              # -2\n~-1                             # 0\n~0                              # -1\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"binary-operators","__idx":34},"children":["Binary operators"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL has the following binary operators, arranged in order of increasing precedence:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"or\nand\n==   !=   <   >   <=   >=   in   not in\n|\n^\n&\n<< >>\n-   +\n*   /   //   %\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Comparison operators, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["in"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["not in"]}," are non-associative,"," ","so the parser will not accept ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0 <= i < n"]},"."," ","All other binary operators of equal precedence associate to the left."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"BinaryExpr = Expression {Binop Expression} .\n\nBinop = 'or'\n      | 'and'\n      | '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in' | 'not' 'in'\n      | '|'\n      | '^'\n      | '&'\n      | '<<' | '>>'\n      | '-' | '+'\n      | '*' | '%' | '/' | '//'\n      .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"or-and-and","__idx":35},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["or"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["and"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["or"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["and"]}," operators yield, respectively, the logical disjunction and"," ","conjunction of their arguments, which need not be Booleans."," ","The expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x or y"]}," yields the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," if its truth value is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["True"]},","," ","or the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]}," otherwise."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"False or False\t\t# False\nFalse or True\t\t# True\nTrue  or False\t\t# True\nTrue  or True\t\t# True\n\n0 or \"hello\"\t\t# \"hello\"\n1 or \"hello\"\t\t# 1\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Similarly, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x and y"]}," yields the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," if its truth value is"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["False"]},", or the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["y"]}," otherwise."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"False and False\t\t# False\nFalse and True\t\t# False\nTrue  and False\t\t# False\nTrue  and True\t\t# True\n\n0 and \"hello\"\t\t# 0\n1 and \"hello\"\t\t# \"hello\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"len(x) > 0 and x[0] == 1\t\t# x[0] is not evaluated if x is empty\nx and x[0] == 1\nlen(x) == 0 or x[0] == \"\"\nnot x or not x[0]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"comparisons","__idx":36},"children":["Comparisons"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}," operator reports whether its operands are equal; the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["!="]}," ","operator is its negation."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The operators ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<="]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">="]}," perform an ordered comparison"," ","of their operands. It is an error to apply these operators to"," ","operands of unequal type, unless one of the operands is an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," and"," ","the other is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},". Of the built-in types, only the following"," ","support ordered comparison, using the ordering relation shown:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"bool            # False < True\nint             # mathematical\nfloat           # as defined by IEEE 754, except NaN > +Inf\nstring          # lexicographical\nbytes           # lexicographical\ntuple           # lexicographical\nlist            # lexicographical\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Comparison of floating-point values follows the IEEE 754 standard"," ","for finite values (including -0.0) and for positive and negative"," ","infinity, but not for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NaN"]}," values, for which the standard behavior"," ","would break several mathematical identities. Thus:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"-Inf < -1e50 < -1.0 < -1e-50 < 0.0 < 1e-50 < 1.0 < 1e50 < +Inf < NaN\n+0.0 == -0.0\nNaN == NaN\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Applications may define additional types that support ordered comparison."," ","The application-defined comparison relation must be a"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings"},"children":["strict weak ordering"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The remaining built-in types support only equality comparisons."," ","Values of type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]}," compare equal if their elements compare"," ","equal, and values of type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["function"]}," are equal only to themselves."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"dict                            # equal contents\nfunction                        # identity\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"arithmetic-operations","__idx":37},"children":["Arithmetic operations"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following table summarizes the binary arithmetic operations"," ","available for built-in types:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Arithmetic (int or float; result has type float unless both operands have type int)\n   number + number              # addition\n   number - number              # subtraction\n   number * number              # multiplication\n   number / number              # floating-point division (result is always a float)\n   number // number             # floored division\n   number % number              # remainder of floored division\n\nBitwise operations:\n   int ^ int                    # bitwise XOR\n   int & int                    # bitwise AND\n   int | int                    # bitwise OR\n   int << int                   # bitwise left shift\n   int >> int                   # bitwise right shift (arithmetic)\n\nSet operations:\n   set | set                    # set union\n   set & set                    # set intersection\n   set - set                    # set difference\n   set ^ set                    # set symmetric difference\n\nConcatenation\n   string + string\n    bytes + bytes\n     list + list\n    tuple + tuple\n\nRepetition (string/bytes/list/tuple)\n      int * sequence\n sequence * int\n\nString interpolation\n   string % any                 # see String Interpolation\n\nSet or dictionary union\n     dict | dict                # see Dictionaries, Sets\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The operands of the arithmetic operators ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["//"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]},","," ","must both be numbers (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},") but need not have the same type."," ","The type of the result has type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," only if both operands have that type."," ","The result of floating-point division ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/"]}," always has type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["&"]}," operator requires two operands of type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},","," ","and yields the bitwise intersection (AND) of its operands."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]}," operator likewise computes bitwise union,"," ","and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["^"]}," operator bitwise XOR (exclusive OR)."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<<"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">>"]}," operators require two operands of type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},"."," ","They shift the first operand to the left or right"," ","by the number of bits given by the second operand."," ","Right shifts are arithmetic, not logical:"," ","they fill the vacated bits with copies of the sign bit."," ","It is a dynamic error if the second operand is negative."," ","Left shifts will also throw an error if the result is not representable using a"," ","signed 64-bit integer."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"0x12345678 & 0xFF               # 0x00000078\n0x12345678 | 0xFF               # 0x123456FF\n0b01011101 ^ 0b110101101        # 0b111110000\n0b01011101 >> 2                 # 0b010111\n0b01011101 << 2                 # 0b0101110100\n-1 >> 100                       # error; shifts over 63 are not allowed\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]}," operator may be applied to non-numeric operands of the same"," ","type, such as two lists, two tuples, two strings, or two bytes, in which case it"," ","computes the concatenation of the two operands and yields a new value of"," ","the same type."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"Hello, \" + \"world\"\t\t# \"Hello, world\"\n(1, 2) + (3, 4)\t\t\t# (1, 2, 3, 4)\n[1, 2] + [3, 4]\t\t\t# [1, 2, 3, 4]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]}," operator may be applied to an integer ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["n"]}," and a value of type"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]},", in which case it yields a new value"," ","of the same sequence type consisting of ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["n"]}," repetitions of the original sequence."," ","The order of the operands is immaterial."," ","Negative values of ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["n"]}," behave like zero."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"'mur' * 2               # 'murmur'\n3 * (True, \"a\")         # (True, \"a\", True, \"a\", True, \"a\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Applications may define additional types that support any subset of"," ","these operators."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"membership-tests","__idx":38},"children":["Membership tests"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"      any in     sequence\t\t(list, tuple, dict, set, string, bytes, range*)\n      any not in sequence\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["in"]}," operator reports whether its first operand is a member of its"," ","second operand, which must be a list, tuple, dict, set, string, bytes, or"," ","range. (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*range"]}," is a special-purpose type returned by the built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["range"]}," ","function; see the Built-in API Reference.)"," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["not in"]}," operator is its negation."," ","Both return a Boolean."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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 or bytes are all its substrings."," ","Additionally, the members of a bytes include the int values of its (byte) elements."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"1 in [1, 2, 3]                  # True\n4 not in (1, 2, 3)              # True\n\nd = {\"one\": 1, \"two\": 2}\n\"one\" in d                      # True\n\"three\" in d                    # False\n1 in d                          # False\n\n\"nasty\" in \"dynasty\"            # True\n\"a\" in \"banana\"                 # True\n\"f\" not in \"way\"                # True\n\nb\"nasty\" in b\"dynasty\"          # True\n97 in b\"abc\"                    # True (97 = 'a')\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"string-interpolation","__idx":39},"children":["String interpolation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["format % args"]}," performs ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["string interpolation"]},", a"," ","simple form of template expansion."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["format"]}," string is interpreted as a sequence of literal portions"," ","and ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["conversions"]},"."," ","Each conversion, which starts with a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," character, is replaced by its"," ","corresponding value from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["args"]},"."," ","The characters following ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," in each conversion determine which"," ","argument it uses and how to convert it to a string."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," character marks the start of a conversion specifier, unless"," ","it is immediately followed by another ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]},", in which cases both"," ","characters together denote a single literal percent sign."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The conversion's operand is the next element of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["args"]},","," ","which must be a tuple with exactly one component per conversion,"," ","unless the format string contains only a single conversion, in which"," ","case ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["args"]}," itself is its operand."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL does not support the flag, width, and padding specifiers"," ","supported by Python's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," and other variants of C's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["printf"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]}," comes a single letter indicating what"," ","operand types are valid and how to convert the operand ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," to a string:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"%       none            literal percent sign\ns       any             as if by str(x)\nr       any             as if by repr(x)\nd       number          signed integer decimal\no       number          signed octal, no 0o prefix\nx       number          signed hexadecimal, lowercase, no 0x prefix\nX       number          signed hexadecimal, uppercase, no 0x prefix\ne       number          float exponential format, lowercase (1.230000e+12)\nE       number          float exponential format, uppercase (1.230000E+12)\nf       number          float decimal format                (1230000000000.000000)\nF       number          same as %f\ng       number          compact format, lowercase           (0.0, 1.1, 1200, 1e+45, 1.2e+12)\nG       number          compact format, uppercase           (0.0, 1.1, 1200, 1e+45, 1.2E+12)\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL only supports the conversion specifiers listed above; ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%c"]}," and"," ","other specifiers found in some implementations are not available."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str(float)"]}," is not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%g"]},": where ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%g"]}," renders six significant digits,"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]}," uses the least precision that still reads back as the same value,"," ","so ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str(1.2345679012345676)"]}," keeps every digit while ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"%g\""]}," of it gives"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1.23457"]},". ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]}," omits unnecessary trailing zeros in the significand"," ","(along with the decimal point itself if the significand has no fraction),"," ","and always contains a decimal point or an exponent and thus unambiguously"," ","denotes a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["float"]},", not an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},". Non-finite values render as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+inf"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-inf"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nan"]}," under both."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is an error if the argument does not have the type required by the"," ","conversion specifier, except that ints may be converted to floats"," ","and floats may be truncated to ints."," ","A Boolean argument is not considered a number."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"Hello %s\" % \"Bob\"                              # \"Hello Bob\"\n\n\"Hello %s, your score is %d\" % (\"Bob\", 75)      # \"Hello Bob, your score is 75\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"coordinates=%s\" % (40, -74)\t# error: too many arguments for format string\n\"coordinates=%s\" % ((40, -74),)\t# \"coordinates=(40, -74)\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"conditional-expressions","__idx":40},"children":["Conditional expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A conditional expression has the form ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a if cond else b"]},"."," ","It first evaluates the condition ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["cond"]},"."," ","If it's true, it evaluates ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a"]}," and yields its value;"," ","otherwise it yields the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["b"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"IfExpr = Expression 'if' Expression 'else' Expression .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"yes\" if enabled else \"no\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["During parsing, the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," operator, considered as a postfix operator on"," ","the \"true\" expression, has higher precedence than ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["else"]}," (a prefix"," ","operator on the \"false\" expression), which in turn has higher"," ","precedence than the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda"]}," prefix operator."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"a if b else (c if d else e)          # parens are redundant\n(a if b else c) if d else e          # parens are required\n\nlambda: (a if b else c)              # parens are redundant\n(lambda: a) if b else c              # parens are required\n\na if b else lambda: (c if d else e)  # parens are redundant\na if b else (lambda: c if d else e)  # parens are required\n(a if b else lambda: c) if d else e  # parens are required\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"comprehensions","__idx":41},"children":["Comprehensions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A comprehension constructs new list or dictionary value by looping"," ","over one or more iterables and evaluating a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["body"]}," expression that produces"," ","successive elements of the result."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A list comprehension consists of a single expression followed by one"," ","or more ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["clauses"]},", the first of which must be a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clause."," ","Each ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clause resembles a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement, and specifies an"," ","iterable operand and a set of variables to be assigned by successive"," ","values of the iterable."," ","An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," clause resembles an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statement, and specifies a condition"," ","that must be met for the body expression to be evaluated."," ","A sequence of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," clauses acts like a nested sequence of"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statements."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"ListComp = '[' Expression {CompClause} ']'.\nDictComp = '{' Entry {CompClause} '}' .\n\nCompClause = 'for' LoopVariables 'in' Expression\n           | 'if' Expression .\n\nLoopVariables = PrimaryExpr {',' PrimaryExpr} .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[x*x for x in range(5)]                 # [0, 1, 4, 9, 16]\n[x*x for x in range(5) if x%2 == 0]     # [0, 4, 16]\n[(x, y) for x in range(5)\n        if x%2 == 0\n        for y in range(5)\n        if y > x]                       # [(0, 1), (0, 2), (0, 3), (0, 4), (2, 3), (2, 4)]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dict comprehension resembles a list comprehension, but its body is a"," ","pair of expressions, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["As with a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement, the loop variables may exploit compound"," ","assignment:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[x*y+z for (x, y), z in [((2, 3), 5), ((\"o\", 2), \"!\")]]         # [11, 'oo!']\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL, following Python 3, does not accept an unparenthesized"," ","tuple as the operand of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clause:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[x*x for x in 1, 2, 3]\t\t# parse error: unexpected comma\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Comprehensions in TSL, 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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x = 1\n_ = [x for x in [2]]            # new variable x is local to the comprehension\nprint(x)                        # 1\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"function-and-method-calls","__idx":42},"children":["Function and method calls"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"CallSuffix = '(' [Arguments [',']] ')' .\n\nArguments = Argument {',' Argument} .\nArgument  = Expression | identifier '=' Expression | '*' Expression | '**' Expression .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A value ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f"]}," of type ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["function"]}," may be called using the expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f(...)"]},"."," ","Applications may define additional types whose values may be called in the same way."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A method call such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["filename.endswith(\".sky\")"]}," is the composition"," ","of two operations, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["m = filename.endswith"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["m(\".sky\")"]},"."," ","The first, a dot operation, yields a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["bound method"]},", a function value"," ","that pairs a receiver value (the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["filename"]}," string) with a choice of"," ","method (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin#endswith"},"children":["string.endswith"]},")."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Only built-in or application-defined types may have methods."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#functions"},"children":["Functions"]}," for an explanation of function parameter passing."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"dot-expressions","__idx":43},"children":["Dot expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dot expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x.f"]}," selects the attribute ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f"]}," (a field or method)"," ","of the value ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Fields are possessed by none of the main TSL ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#data-types"},"children":["data types"]},","," ","but some application-defined types have them."," ","Methods belong to the built-in types ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["set"]}," ","and to many application-defined types."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"DotSuffix = '.' identifier .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dot expression fails if the value does not have an attribute of the"," ","specified name."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the built-in function ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["hasattr(x, \"f\")"]}," to ascertain whether a"," ","value has a specific attribute, or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dir(x)"]}," to enumerate all its"," ","attributes. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["getattr(x, \"f\")"]}," function can be used to select an"," ","attribute when the name ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"f\""]}," is not known statically."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A dot expression that selects a method typically appears within a call"," ","expression, as in these examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"[\"able\", \"baker\", \"charlie\"].index(\"baker\")     # 1\n\"banana\".count(\"a\")                             # 3\n\"banana\".reverse()                              # error: string has no .reverse field or method\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["But when not called immediately, the dot expression evaluates to a"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["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:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"f = \"banana\".count\nf                                               # <built-in method count of string value>\nf(\"a\")                                          # 3\nf(\"n\")                                          # 2\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"subscript-expressions","__idx":44},"children":["Subscript expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A subscript expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[x]"]}," retrieves a value identified by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x"]}," from the"," ","value ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"SubscriptSuffix = '[' Expressions ']' .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There are two main cases: indexing a sequence, and retrieving values of a"," ","mapping."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a"]}," is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]}," (such as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tuple"]},"), or another indexable"," ","object like ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},", the expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[i]"]}," yields the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]},"th element"," ","of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a"]},". Here, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]}," must be an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]}," value in the range ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-n"]}," ≤ ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]}," < ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]},", where"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["len(a)"]},"; any other index results in an error."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A valid negative index ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]}," behaves like the non-negative index ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n+i"]},","," ","allowing for convenient indexing relative to the end of the"," ","sequence."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"abc\"[0]                        # \"a\"\n\"abc\"[1]                        # \"b\"\n\"abc\"[-1]                       # \"c\"\n\n(\"zero\", \"one\", \"two\")[0]       # \"zero\"\n(\"zero\", \"one\", \"two\")[1]       # \"one\"\n(\"zero\", \"one\", \"two\")[-1]      # \"two\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For a subscript expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["m[k]"]},", where the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["m"]}," is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Mapping"]}," ","(most commonly a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict"]},") or any other mapping-like object, the expression"," ","retrieves the value that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["m"]}," associates with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["k"]},". It is an error if ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["m"]}," ","contains no such key ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["k"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A subscript expression appearing on the left side of an assignment causes"," ","the specified sequence element or mapping value to be updated:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"a = list(range(10, 13))  # a == [10, 11, 12]\na[2] = 7                 # a == [10, 11, 7]\n\ncoins = {}               # coins == {}\ncoins[\"suzie b\"] = 100   # coins == {\"suzie b\": 100}\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL does not have a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["del"]}," statement like Python. Deleting the value"," ","associated with an index or key requires calling a method on the containing"," ","object."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"slice-expressions","__idx":45},"children":["Slice expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The slice expression acts as a sort of indexing subscript expression that"," ","retrieves a range of elements rather than a single element. It applies to types"," ","that can be indexed with an integer as above, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Sequence"]},"s, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]},","," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bytes"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"SubscriptSuffix = '[' [Expressions] [':' Expression [':' Expression]] ']' .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A slice expression ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a[start:stop:stride]"]}," yields a copy of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a"]}," containing"," ","exactly the values whose indices are in the specified range. Conceptually,"," ","the range begins at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["start"]},", progresses in increments of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stride"]},", and"," ","ends before reaching ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stop"]},", without ever going out-of-bounds."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["start"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stop"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stride"]}," operands may be ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]}," or omitted"," ","entirely; otherwise, they must evaluate to integers. If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stride"]}," is omitted"," ","then the second colon may optionally also be omitted. (The first colon is"," ","syntactically required in order to distinguish from a subscript expression.)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stride"]}," is omitted its value defaults to 1. It is an error to specify a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stride"]}," of 0."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The effective start and stop indices are computed from the three"," ","operands as follows. Let ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]}," be the length of the sequence."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["If the stride is positive:"]}," ","If the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["start"]}," operand was omitted, it defaults to -infinity."," ","If the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["end"]}," operand was omitted, it defaults to +infinity."," ","For either operand, if a negative value was supplied, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]}," is added to it."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["start"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["end"]}," values are then \"clamped\" to the"," ","nearest value in the range 0 to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]},", inclusive."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["If the stride is negative:"]}," ","If the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["start"]}," operand was omitted, it defaults to +infinity."," ","If the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["end"]}," operand was omitted, it defaults to -infinity."," ","For either operand, if a negative value was supplied, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]}," is added to it."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["start"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["end"]}," values are then \"clamped\" to the"," ","nearest value in the range -1 to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["n"]},"-1, inclusive."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"\"abc\"[1:]               # \"bc\"  (remove first element)\n\"abc\"[:-1]              # \"ab\"  (remove last element)\n\"abc\"[1:-1]             # \"b\"   (remove first and last element)\n\"banana\"[1::2]          # \"aaa\" (select alternate elements starting at index 1)\n\"banana\"[4::-2]         # \"nnb\" (select alternate elements in reverse, starting at index 4)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike Python, TSL does not allow a slice expression to be the target of"," ","an assignment, although it may appear as a subexpression in the target."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Slicing a tuple, string, or bytes may be more efficient than slicing a list"," ","because tuple, string, and bytes values 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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"lambda-expressions","__idx":46},"children":["Lambda expressions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda"]}," expression yields a new function value."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"LambdaExpr = 'lambda' [LambdaParameters] ':' Expression .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lambda parameters use the same forms as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," parameters except that they do"," ","not accept type annotations. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#type-annotations"},"children":["Type annotations"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Syntactically, a lambda expression consists of the keyword ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda"]},","," ","followed by a parameter list like that of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statement but"," ","unparenthesized, then a colon ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[":"]},", and a single expression, the"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["function body"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def map(f, arr):\n    return [f(x) for x in arr]\n\nmap(lambda x: 2*x, range(3))    # [2, 4, 6]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["As with functions created by a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statement, a lambda function"," ","captures the syntax of its body, the default values of any optional"," ","parameters, a reference to each free variable appearing in its body, and"," ","the global dictionary of the current module."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The name of a function created by a lambda expression is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"lambda\""]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The two statements below are essentially equivalent, but the"," ","function created by the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statement is named ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["twice"]}," and the"," ","function created by the lambda expression is named ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def twice(x):\n   return x * 2\n\ntwice = lambda x: x * 2\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"statements","__idx":47},"children":["Statements"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Statement  = DefStmt | IfStmt | ForStmt | WhileStmt | TryStmt | SimpleStmt .\nSimpleStmt = SmallStmt {';' SmallStmt} [';'] '\\n' .\nSmallStmt  = ReturnStmt\n           | BreakStmt | ContinueStmt | PassStmt\n           | AssignStmt\n           | ExprStmt\n           | ImportStmt\n           | RaiseStmt\n           .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pass-statements","__idx":48},"children":["Pass statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pass"]}," statement does nothing. Use a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pass"]}," statement when the"," ","syntax requires a statement but no behavior is required, such as the"," ","body of a function that does nothing."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"PassStmt = 'pass' .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def noop():\n   pass\n\ndef list_to_dict(items):\n  # Convert list of tuples to dict\n  m = {}\n  for k, m[k] in items:\n    pass\n  return m\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"assignments","__idx":49},"children":["Assignments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An assignment statement has the form ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"AssignStmt = Expressions '=' Expressions .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The expression on the left-hand side is called a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["target"]},". The"," ","simplest target is the name of a variable, but a target may also have"," ","the form of a subscript expression to update the element of a list or"," ","dictionary, or the form of a dot expression to update the field of an"," ","object. These forms may be nested."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"k = 1\na[i] = v\no.f = \"\"\no.g(1)[2] = None\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Compound targets may consist of a comma-separated list of"," ","subtargets, optionally surrounded by parentheses or square brackets. These too may be nested."," ","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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"a, b = 2, 3\n(x, y) = f()\n[zero, one, two] = range(3)\n[] = ()\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The same process for assigning a value to a target expression is used"," ","in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statements and in comprehension ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," clauses."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A simple variable assignment may also carry an optional type annotation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"AnnotatedAssignStmt = identifier ':' Type ['=' Expressions] .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"age: int = 30\nnames: list[str] = [\"alice\", \"bob\"]\nflag: bool                          # bare annotation, no assignment\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The type annotation is parsed but otherwise has no runtime effect; the"," ","right-hand side, when present, is assigned to the target exactly as in a"," ","regular assignment statement. The target of an annotated assignment must be a"," ","single identifier - compound targets are not supported in this form. See"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#type-annotations"},"children":["Type annotations"]}," for the grammar of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Type"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"augmented-assignments","__idx":50},"children":["Augmented assignments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An augmented assignment, which has the form ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lhs op= rhs"]}," updates the"," ","variable ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lhs"]}," by applying a binary arithmetic operator ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["op"]}," (one of"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["//"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["&"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["^"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<<"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[">>"]},") to the"," ","previous value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lhs"]}," and the value of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rhs"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"AssignStmt = Expressions ('=' | '+=' | '-=' | '*=' | '/=' | '//=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=') Expressions .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The left-hand side must be a simple target:"," ","a name, a subscript expression, or a dot expression."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x -= 1\nx.filename += \".sky\"\na[index()] *= 2\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any subexpressions in the target on the left-hand side are evaluated"," ","exactly once, before the evaluation of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rhs"]},"."," ","The first two assignments above are thus equivalent to:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x = x - 1\nx.filename = x.filename + \".sky\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["and the third assignment is similar in effect to the following two"," ","statements but does not declare a new temporary variable ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["i"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"i = index()\na[i] = a[i] * 2\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"function-definitions","__idx":51},"children":["Function definitions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statement creates a named function and assigns it to a variable."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"DefStmt = 'def' identifier '(' [Parameters [',']] ')' ['->' Type] ':' Suite .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A function's parameters and return value may carry optional type annotations."," ","See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#type-annotations"},"children":["Type annotations"]}," for details."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def twice(x):\n    return x * 2\n\nstr(twice)              # \"<function f>\"\ntwice(2)                # 4\ntwice(\"two\")            # \"twotwo\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The function's name is preceded by the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The parameter list is a comma-separated list whose elements are of"," ","several kinds. First come zero or more required parameters, which are"," ","simple identifiers; all calls must provide an argument value for these parameters."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The required parameters are followed by zero or more optional"," ","parameters, of the form ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name=expression"]},". The expression specifies"," ","the default value for the parameter for use in calls that do not"," ","provide an argument value for it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The required parameters are optionally followed by a single parameter"," ","name preceded by a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]},". This is the called the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["varargs"]}," parameter,"," ","and it accumulates surplus positional arguments specified by a call."," ","It is conventionally named ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The varargs parameter may be followed by zero or more"," ","parameters, again of the forms ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name=expression"]},","," ","but these parameters differ from earlier ones in that they are"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["keyword-only"]},": if a call provides their values, it must do so as"," ","keyword arguments, not positional ones."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Note that even though keyword-only arguments are declared after ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," in a"," ","function's definition, they nevertheless must appear before ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," in a call"," ","to the function."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def g(a, *args, b=2, c):\n  print(a, b, c, args)\n\ng(1, 3)                 # error: function g missing 1 argument (c)\ng(1, *[4, 5], c=3)      # error: keyword argument c may not follow *args\ng(1, 4, c=3)            # \"1 2 3 (4,)\"\ng(1, c=3, *[4, 5])      # \"1 2 3 (4, 5)\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A non-variadic function may also declare keyword-only parameters,"," ","by using a bare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*"]}," in place of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["*args"]}," parameter."," ","This form does not declare a parameter but marks the boundary"," ","between the earlier parameters and the keyword-only parameters."," ","This form must be followed by at least one keyword-only parameter."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(a, *, b=2, c):\n  print(a, b, c)\n\nf(1)                    # error: function f missing 1 argument (c)\nf(1, 3)                 # error: function f accepts 1 positional argument (2 given)\nf(1, c=3)               # \"1 2 3\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Finally, there may be an optional parameter name preceded by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["**"]},"."," ","This is called the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["keyword arguments"]}," parameter, and accumulates in a"," ","dictionary any surplus ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name=value"]}," arguments that do not match a"," ","prior parameter. It is conventionally named ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["**kwargs"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here are some example parameter lists:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(): pass\ndef f(a, b, c): pass\ndef f(a, b, c=1): pass\ndef f(a, b, c=1, *args): pass\ndef f(a, b, c=1, *args, **kwargs): pass\ndef f(**kwargs): pass\ndef f(a, b, c=1, *, d=1): pass\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Execution of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def"]}," statement creates a new function object. The"," ","function object contains: the syntax of the function body; the default"," ","value for each optional parameter; a reference to each free variable"," ","appearing within the function body; and the global dictionary of the"," ","current module."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def f(x):\n  res = []\n  def get_x():\n    res.append(x)\n  get_x()\n  x = 2\n  get_x()\n\nf(1) # returns [1, 2]\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"type-annotations","__idx":52},"children":["Type annotations"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL accepts optional type annotations on function parameters, function return"," ","types, and variable assignments. Annotations are parsed but otherwise have no"," ","runtime effect today. They serve as documentation and a foothold for future"," ","linting."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Parameter = identifier\n          | identifier '=' Expression\n          | identifier ':' Type\n          | identifier ':' Type '=' Expression\n          | '*'\n          | '*' identifier\n          | '**' identifier\n          .\n\nType      = TypeAtom\n          | Type '|' TypeAtom\n          .\n\nTypeAtom  = identifier\n          | TypeAtom '.' identifier\n          | TypeAtom '[' TypeArgs ']'\n          .\n\nTypeArgs  = Type {',' Type} .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"def greet(name: str, count: int = 1) -> str:\n    return name * count\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The type sublanguage is intentionally narrower than ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Expression"]},". Permitted"," ","forms are:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Bare identifiers (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["None"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MyType"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Dotted names referring to a member of an imported module (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mod.Foo"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Index expressions (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["list[int]"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dict[str, int]"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Unions with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int | str"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["int | str | None"]},")"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Arbitrary expressions are not allowed in type position: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def f() -> 1 + 1:"]}," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["def f(x: foo()):"]}," are syntax errors."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Identifiers used in type annotations are not required to resolve to anything"," ","in scope. Writing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x: NotDefined"]}," is legal even when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NotDefined"]}," has no"," ","binding. This lets users adopt a project's type vocabulary incrementally"," ","without import churn."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lambda parameters do not accept type annotations: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lambda x: int: x"]}," is a"," ","syntax error (the second ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[":"]}," would be ambiguous with the lambda body"," ","separator)."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"return-statements","__idx":53},"children":["Return statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["return"]}," statement ends the execution of a function and returns a"," ","value to the caller of the function."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"ReturnStmt = 'return' [Expressions] .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A return statement may have zero, one, or more"," ","result expressions separated by commas."," ","With no expressions, the function has the result ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"return                  # returns None\nreturn 1                # returns 1\nreturn 1, 2             # returns (1, 2)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"expression-statements","__idx":54},"children":["Expression statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An expression statement evaluates an expression and discards its result."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"ExprStmt = Expressions .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any expression may be used as a statement, but an expression statement is"," ","most often used to call a function for its side effects."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"my_list.append(1)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"if-statements","__idx":55},"children":["If statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statement evaluates an expression (the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["condition"]},"), then, if"," ","the truth value of the condition is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["True"]},", executes a list of"," ","statements."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"IfStmt = 'if' Expression ':' Suite {'elif' Expression ':' Suite} ['else' ':' Suite] .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"if score >= 100:\n    print(\"You win!\")\n    return\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statement may have an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["else"]}," block defining a second list of"," ","statements to be executed if the condition is false."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"if score >= 100:\n        print(\"You win!\")\n        return\nelse:\n        print(\"Keep trying...\")\n        continue\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is common for the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["else"]}," block to contain another ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statement."," ","To avoid increasing the nesting depth unnecessarily, the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["else"]}," and"," ","following ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," may be combined as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["elif"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"if x > 0:\n        result = 1\nelif x < 0:\n        result = -1\nelse:\n        result = 0\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statement is permitted at the top level of a module, in the"," ","global scope, as well as inside function definitions."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"for-statements","__idx":56},"children":["For statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement evaluates its operand, which must be an iterable value."," ","Then, for each element of the iterable, the loop assigns"," ","the successive element values to one or more variables and executes a"," ","list of statements, the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["loop body"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"ForStmt = 'for' LoopVariables 'in' Expressions ':' Suite .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"for x in range(10):\n   print(10)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"for a, i in [[\"a\", 1], [\"b\", 2], [\"c\", 3]]:\n  print(a, i)                          # prints \"a 1\", \"b 2\", \"c 3\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL does not guarantee that iterable types are finite."," ","It is up to the runtime implementation to impose bounds on any scripts"," ","run, either through iteration bounds or through time bounds."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Within the body of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," loop, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," statements may"," ","be used to stop the execution of the loop or advance to the next"," ","iteration."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," statement is permitted at the top level of a module as well"," ","as inside function definitions."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"break-and-continue","__idx":57},"children":["Break and Continue"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," statements terminate the current iteration"," ","of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," loop. Whereas the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," statement resumes the loop at"," ","the next iteration, a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," statement terminates the entire loop."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"BreakStmt    = 'break' .\nContinueStmt = 'continue' .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"for x in range(10):\n    if x%2 == 1:\n        continue        # skip odd numbers\n    if x > 7:\n        break           # stop at 8\n    print(x)            # prints \"0\", \"2\", \"4\", \"6\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both statements affect only the innermost lexically enclosing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," loop."," ","It is a static error to use a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," statement outside a"," ","loop."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"while-statements","__idx":58},"children":["While statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," statement executes its body repeatedly as long as the"," ","condition expression evaluates to a true value."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"WhileStmt = 'while' Expression ':' Suite .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"x = 0\nwhile x < 5:\n    print(x)\n    x += 1              # prints \"0\", \"1\", \"2\", \"3\", \"4\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Within the body of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," loop, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," statements may"," ","be used to stop the execution of the loop or advance to the next"," ","iteration."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," loops, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," loops are not guaranteed to terminate."," ","It is the programmer's responsibility to ensure that the loop condition"," ","eventually becomes false, or that the loop body executes a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]}," ","statement."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," statement is permitted at the top level of a module as well"," ","as inside function definitions."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"import-statements","__idx":59},"children":["Import statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement loads another TSL module and binds it to a name"," ","in the current module. All symbols from the imported module must be"," ","accessed through the module name, although the module name may be aliased."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"ImportStmt = 'import' (identifier | string) ['as' identifier] .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement names a single module to load. An optional ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["as"]}," ","clause provides an alias for the module name in the current scope."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import os                     # binds the module to the name \"os\"\nimport fs as filesystem       # binds the module to the name \"filesystem\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike Python, TSL does not support ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["from X import Y"]}," syntax."," ","You must import a whole module and always qualify symbol references"," ","through the module name (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["os.stat(path)"]},")."," ","You also cannot import multiple modules on a single line; each"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," must appear as its own statement."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["During execution, the application determines what action to take for an"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement."," ","A typical implementation locates and executes a TSL 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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement within a function is a static error. An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," ","statement ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["can"]}," be within a globally-scoped ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," statement:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import os\nif os.platform == os.PLATFORM_WINDOWS:\n    import windows\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"try-statements","__idx":60},"children":["Try statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," statement runs a block of code with one or more handlers for"," ","exceptions raised during execution. It is a Tanium extension to Starlark."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"TryStmt      = 'try' ':' Suite ExceptClause {ExceptClause} ['finally' ':' Suite]\n             | 'try' ':' Suite 'finally' ':' Suite .\nExceptClause = 'except' [Expression ['as' identifier]] ':' Suite .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," statement evaluates the body. If the body raises an exception, the"," ","exception is matched in order against each ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause; the first matching"," ","clause's body is executed, and the exception is considered handled. If no"," ","clause matches, the exception continues propagating."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A bare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except:"]}," (with no type expression) matches every exception. An"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause with a type expression matches only exceptions whose type is"," ","that type or one of its descendants in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#exception-types"},"children":["exception"," ","hierarchy"]},". The type expression may also be a tuple of"," ","exception types; the clause matches if the exception is an instance of any of"," ","them."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"try:\n    rows = tanium.query_sql(sql)\nexcept IOError:\n    rows = []\nexcept (KeyError, ValueError):\n    rows = []\nexcept:\n    log.error(\"unexpected failure\")\n    rows = []\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike Python, TSL has no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["else"]}," clause on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," statements. Code that"," ","should run only after a successful ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," body can simply follow the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," ","statement (when no other paths exist) or be guarded by an explicit success"," ","flag."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The optional ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," clause runs unconditionally - after a successful body,"," ","after a handled exception, and even when an unhandled exception is propagating."," ","A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," statement may pair a body with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally:"]}," alone, with no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," ","clauses."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"f = fs.open(path)\ntry:\n    process(f)\nfinally:\n    f.close()\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," itself raises, the new exception replaces any in-flight"," ","exception. A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["return"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["break"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," inside ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," discards any"," ","in-flight exception or pending control transfer from the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," ","body. These behaviors mirror Python."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It is a static error to use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["continue"]}," inside a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," block."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"variable-scoping","__idx":61},"children":["Variable scoping"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," bodies do not introduce a new lexical scope."," ","Variables assigned anywhere inside a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," statement are visible in subsequent"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clauses, in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," clause, and after the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," statement"," ","ends, just as they would be for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]}," bodies."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A consequence: a variable assigned only inside the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]}," body may be ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["unbound"]}," ","when the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," clause runs, because the exception could have fired before"," ","the assignment completed. Reading an unbound variable is a runtime error. The"," ","typical idiom is to acquire the resource ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["before"]}," entering the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]},", so the"," ","binding is unconditionally established by the time ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["finally"]}," executes:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"f = fs.open(path)        # acquire before try\ntry:\n    process(f)\nfinally:\n    f.close()            # f is always bound here\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except T as name:"]}," binding is the one exception to this rule - it is"," ","scoped to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause body. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#exception-binding"},"children":["Exception binding"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"exception-binding","__idx":62},"children":["Exception binding"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except T as name:"]}," clause binds the caught exception value to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name"]}," for"," ","the duration of the clause body. The binding is removed when the clause exits"," ","(matching Python's behavior - the name does not survive the block). Referring"," ","to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name"]}," outside the clause is a static error within the same function."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"try:\n    parse(input)\nexcept ValueError as e:\n    log.warn(\"parse failed: \" + e.message)\n# e is not in scope here\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"exception-matching","__idx":63},"children":["Exception matching"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The expression in an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause is evaluated when the clause is reached."," ","It must yield an exception type (or a tuple of exception types). If it does"," ","not, a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TypeError"]}," is raised at the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," site."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because the expression is evaluated normally, module-exported exception types"," ","work without special syntax, including under aliasing imports:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import wmi as winmgmt\ntry:\n    rows = winmgmt.query(q)\nexcept winmgmt.QueryError as e:\n    log.warn(e.message)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"re-raising","__idx":64},"children":["Re-raising"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Inside an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause, a bare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise"]}," re-raises the currently active"," ","exception. Outside an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause, bare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise"]}," is a static error. See"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#raise-statements"},"children":["Raise statements"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"raise-statements","__idx":65},"children":["Raise statements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise"]}," statement signals an exception. It is a Tanium extension to"," ","Starlark."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"RaiseStmt = 'raise' [Expression] .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["User code may only raise the base ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Exception"]}," type:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"raise Exception(\"something went wrong\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Specialized builtin types (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KeyError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ValueError"]},", etc.) and module-exported"," ","exception types are visible only as match targets in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clauses. They"," ","are not constructible from script code; attempting to call them raises a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TypeError"]}," at the call site. This is a deliberate departure from Python and"," ","discourages exceptions-as-control-flow patterns: the language exposes specific"," ","types for ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["handling"]}," stdlib errors, not for user-defined signaling."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A bare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise"]}," (no expression) re-raises the currently active exception. It"," ","is a static error to use bare ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise"]}," outside an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clause."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"try:\n    risky()\nexcept IOError as e:\n    log.warn(\"retry: \" + e.message)\n    raise           # re-raise the IOError\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The legacy ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fail(value)"]}," builtin remains and is equivalent to"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise Exception(value)"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"exception-types","__idx":66},"children":["Exception types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL provides a fixed hierarchy of builtin exception types:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"Exception\n├── RuntimeError\n├── ValueError\n├── TypeError\n├── KeyError\n├── IndexError\n├── LookupError\n├── IOError\n└── ArithmeticError\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Exception"]}," is the only builtin that is constructible from script code (via"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raise Exception(\"message\")"]}," or as a callable: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Exception(\"message\")"]},"). All"," ","other names in the hierarchy are bound in the predeclared environment for the"," ","sole purpose of appearing in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," clauses."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Native modules may export additional exception types via the host"," ","registration API; such types are documented as attributes of the module."," ","Module-exported types are subtypes of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Exception"]}," (or, where appropriate, of"," ","a more specific builtin) and are not constructible from script code. Scripts"," ","match on them just like builtin types:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import fs\ntry:\n    fs.list_dir(path)\nexcept fs.NotFound:\n    log.info(\"missing directory; skipping\")\nexcept IOError as e:\n    log.warn(e.message)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["User-defined exception classes are not supported; there is no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["class"]}," ","statement in TSL."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"exception-values","__idx":67},"children":["Exception values"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A raised exception produces an ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["exception value"]},", distinct from the exception"," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["type"]},". Exception values have the following attributes:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Attribute"},"children":["Attribute"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["message"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Human-readable description (string)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["args"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Tuple of arguments passed at construction."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["traceback"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Captured TSL call stack at the point the exception was raised."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Exception values are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["non-freezable"]},": they cannot be assigned to a"," ","module-level name that survives module evaluation, and therefore cannot be"," ","exported from a module. Attempting to freeze a module that retains a"," ","reference to an exception value is a runtime error reported at freeze time."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is in contrast to exception ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["types"]},", which are ordinary frozen module"," ","attributes intended to be exported by native modules."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"module-execution","__idx":68},"children":["Module execution"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each TSL file defines a ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["module"]},", which is a mapping from the"," ","names of global variables to their values."," ","When a TSL file is executed, whether directly by the application"," ","or indirectly through an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement, a new TSL thread is"," ","created, and this thread executes all the top-level statements in the"," ","file."," ","TSL allows ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["if"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["for"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["while"]}," statements at the top level,"," ","so control flow at the module level may include branching and looping."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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 TSL thread, such as"," ","one executing an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statement."," ","Such threads may access values or call functions defined in the loaded"," ","module."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A TSL 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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If a TSL 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 TSL module, any"," ","active ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["import"]}," statements waiting for initialization of the module also"," ","fail."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["TSL provides no mechanism by which errors can be handled within"," ","the language."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"script-exit","__idx":69},"children":["Script exit"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A script may terminate early by calling the built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["exit(code)"]}," ","function, where ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["code"]}," is a required integer argument in the int32"," ","range (-2147483648 to 2147483647). Calling ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["exit"]}," immediately stops"," ","execution of the entire script, including unwinding through nested"," ","function calls, and sets the script's exit code to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["code"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A script that completes normally without calling ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["exit"]}," has an implicit"," ","exit code of 0."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["exit"]}," is a controlled termination, not an error. It cannot be"," ","intercepted by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["try"]},"/",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["except"]}," statements or the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["catch()"]}," built-in."," ","This distinguishes it from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fail()"]},", which raises a catchable error."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"exit(0)           # terminate successfully\nexit(1)           # terminate with failure code\n\ndef cleanup():\n    exit(42)      # terminates the entire script, not just this function\n\ncatch(lambda: exit(1))   # script terminates with exit code 1; catch does not intercept\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The exit code is visible to the caller of the script execution API."," ","When a TSL script is executed as a package action, the exit code is"," ","reported in the package execution result and evaluated against the"," ","action's exit code criteria."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"built-in-constants-functions-and-methods","__idx":70},"children":["Built-in constants, functions, and methods"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/custom-content/tsl/modules/builtin"},"children":["TSL Built-in API Reference"]}," for documentation of all"," ","built-in constants, functions, and type methods."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"grammar-reference","__idx":71},"children":["Grammar reference"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"File = {Statement | newline} eof .\n\nStatement = DefStmt | IfStmt | ForStmt | WhileStmt | TryStmt | SimpleStmt .\n\nDefStmt = 'def' identifier '(' [Parameters [',']] ')' ['->' Type] ':' Suite .\n\nParameters = Parameter {',' Parameter}.\n\nParameter  = identifier\n           | identifier '=' Expression\n           | identifier ':' Type\n           | identifier ':' Type '=' Expression\n           | '*'\n           | '*' identifier\n           | '**' identifier\n           .\n\nType     = TypeAtom | Type '|' TypeAtom .\nTypeAtom = identifier | TypeAtom '.' identifier | TypeAtom '[' TypeArgs ']' .\nTypeArgs = Type {',' Type} .\n\nIfStmt = 'if' Expression ':' Suite {'elif' Expression ':' Suite} ['else' ':' Suite] .\n\nForStmt = 'for' LoopVariables 'in' Expressions ':' Suite .\n\nWhileStmt = 'while' Expression ':' Suite .\n\nTryStmt = 'try' ':' Suite ExceptClause {ExceptClause} ['finally' ':' Suite]\n        | 'try' ':' Suite 'finally' ':' Suite .\n\nExceptClause = 'except' [Expression ['as' identifier]] ':' Suite .\n\nSuite = [newline indent {Statement} outdent] | SimpleStmt .\n\nSimpleStmt = SmallStmt {';' SmallStmt} [';'] '\\n' .\n# NOTE: '\\n' optional at EOF\n\nSmallStmt = ReturnStmt\n          | BreakStmt | ContinueStmt | PassStmt\n          | AssignStmt\n          | AnnotatedAssignStmt\n          | ExprStmt\n          | ImportStmt\n          | RaiseStmt\n          .\n\nReturnStmt          = 'return' [Expressions] .\nBreakStmt           = 'break' .\nContinueStmt        = 'continue' .\nPassStmt            = 'pass' .\nAssignStmt          = Expressions ('=' | '+=' | '-=' | '*=' | '/=' | '//=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=') Expressions .\nAnnotatedAssignStmt = identifier ':' Type ['=' Expressions] .\nExprStmt            = Expressions .\nRaiseStmt           = 'raise' [Expression] .\n\nImportStmt = 'import' ( identifier | string ) ['as' identifier] .\n\nExpression = IfExpr | PrimaryExpr | UnaryExpr | BinaryExpr | LambdaExpr .\n\nIfExpr = Expression 'if' Expression 'else' Expression .\n\nPrimaryExpr = Operand\n            | PrimaryExpr DotSuffix\n            | PrimaryExpr CallSuffix\n            | PrimaryExpr SubscriptSuffix\n            .\n\nOperand = identifier\n        | int | float | string | bytes\n        | ListExpr | ListComp\n        | DictExpr | DictComp\n        | '(' [Expressions [',']] ')'\n        .\n\nDotSuffix   = '.' identifier .\nSubscriptSuffix = '[' [Expressions] [':' Expression [':' Expression]] ']'\n                | '[' Expressions ']'\n                .\nCallSuffix  = '(' [Arguments [',']] ')' .\n\nArguments = Argument {',' Argument} .\nArgument  = Expression | identifier '=' Expression | '*' Expression | '**' Expression .\n\nListExpr = '[' [Expressions [',']] ']' .\nListComp = '[' Expression {CompClause} ']'.\n\nDictExpr = '{' [Entries [',']] '}' .\nDictComp = '{' Entry {CompClause} '}' .\nEntries  = Entry {',' Entry} .\nEntry    = Expression ':' Expression .\n\nCompClause = 'for' LoopVariables 'in' Expression | 'if' Expression .\n\nUnaryExpr = '+' Expression\n          | '-' Expression\n          | '~' Expression\n          | 'not' Expression\n          .\n\nBinaryExpr = Expression {Binop Expression} .\n\nBinop = 'or'\n      | 'and'\n      | '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in' | 'not' 'in'\n      | '|'\n      | '^'\n      | '&'\n      | '<<' | '>>'\n      | '-' | '+'\n      | '*' | '%' | '/' | '//'\n      .\n\nLambdaExpr = 'lambda' [LambdaParameters] ':' Expression .\n\n# LambdaParameters mirrors Parameters but does not permit type annotations.\nLambdaParameters = LambdaParameter {',' LambdaParameter} .\nLambdaParameter  = identifier\n                 | identifier '=' Expression\n                 | '*'\n                 | '*' identifier\n                 | '**' identifier\n                 .\n\nExpressions = Expression {',' Expression} .\n# NOTE: trailing comma permitted only when within [...] or (...).\n\nLoopVariables = PrimaryExpr {',' PrimaryExpr} .\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tokens:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["spaces: newline, eof, indent, outdent."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["identifier."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["literals: string, bytes, int, float."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["plus all quoted tokens such as '+=', 'return'."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Notes:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Ambiguity is resolved using operator precedence."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The grammar does not enforce the legal order of params and args,"," ","nor that the first CompClause must be a 'for'."]}]}]},"headings":[{"value":"Specification","id":"specification","depth":1},{"value":"Lexical elements","id":"lexical-elements","depth":2},{"value":"String literals","id":"string-literals","depth":3},{"value":"String escapes","id":"string-escapes","depth":4},{"value":"Bytes literals","id":"bytes-literals","depth":3},{"value":"Special tokens","id":"special-tokens","depth":3},{"value":"Data types","id":"data-types","depth":2},{"value":"None","id":"none","depth":3},{"value":"Booleans","id":"booleans","depth":3},{"value":"Integers","id":"integers","depth":3},{"value":"Floating-point numbers","id":"floating-point-numbers","depth":3},{"value":"Strings","id":"strings","depth":3},{"value":"Bytes","id":"bytes","depth":3},{"value":"Lists","id":"lists","depth":3},{"value":"Tuples","id":"tuples","depth":3},{"value":"Dictionaries","id":"dictionaries","depth":3},{"value":"Sets","id":"sets","depth":3},{"value":"Functions","id":"functions","depth":3},{"value":"Built-in functions","id":"built-in-functions","depth":3},{"value":"Name binding and variables","id":"name-binding-and-variables","depth":2},{"value":"Value concepts","id":"value-concepts","depth":2},{"value":"Identity and mutation","id":"identity-and-mutation","depth":3},{"value":"Freezing a value","id":"freezing-a-value","depth":3},{"value":"Hashing","id":"hashing","depth":3},{"value":"Collection types","id":"collection-types","depth":3},{"value":"Iteration","id":"iteration","depth":3},{"value":"Indexing","id":"indexing","depth":3},{"value":"Expressions","id":"expressions","depth":2},{"value":"Identifiers","id":"identifiers","depth":3},{"value":"Literals","id":"literals","depth":3},{"value":"Parenthesized expressions","id":"parenthesized-expressions","depth":3},{"value":"Dictionary expressions","id":"dictionary-expressions","depth":3},{"value":"List expressions","id":"list-expressions","depth":3},{"value":"Unary operators","id":"unary-operators","depth":3},{"value":"Binary operators","id":"binary-operators","depth":3},{"value":"or and and","id":"or-and-and","depth":4},{"value":"Comparisons","id":"comparisons","depth":4},{"value":"Arithmetic operations","id":"arithmetic-operations","depth":4},{"value":"Membership tests","id":"membership-tests","depth":4},{"value":"String interpolation","id":"string-interpolation","depth":4},{"value":"Conditional expressions","id":"conditional-expressions","depth":3},{"value":"Comprehensions","id":"comprehensions","depth":3},{"value":"Function and method calls","id":"function-and-method-calls","depth":3},{"value":"Dot expressions","id":"dot-expressions","depth":3},{"value":"Subscript expressions","id":"subscript-expressions","depth":3},{"value":"Slice expressions","id":"slice-expressions","depth":3},{"value":"Lambda expressions","id":"lambda-expressions","depth":3},{"value":"Statements","id":"statements","depth":2},{"value":"Pass statements","id":"pass-statements","depth":3},{"value":"Assignments","id":"assignments","depth":3},{"value":"Augmented assignments","id":"augmented-assignments","depth":3},{"value":"Function definitions","id":"function-definitions","depth":3},{"value":"Type annotations","id":"type-annotations","depth":3},{"value":"Return statements","id":"return-statements","depth":3},{"value":"Expression statements","id":"expression-statements","depth":3},{"value":"If statements","id":"if-statements","depth":3},{"value":"For statements","id":"for-statements","depth":3},{"value":"Break and Continue","id":"break-and-continue","depth":3},{"value":"While statements","id":"while-statements","depth":3},{"value":"Import statements","id":"import-statements","depth":3},{"value":"Try statements","id":"try-statements","depth":3},{"value":"Variable scoping","id":"variable-scoping","depth":4},{"value":"Exception binding","id":"exception-binding","depth":4},{"value":"Exception matching","id":"exception-matching","depth":4},{"value":"Re-raising","id":"re-raising","depth":4},{"value":"Raise statements","id":"raise-statements","depth":3},{"value":"Exception types","id":"exception-types","depth":3},{"value":"Exception values","id":"exception-values","depth":4},{"value":"Module execution","id":"module-execution","depth":2},{"value":"Script exit","id":"script-exit","depth":3},{"value":"Built-in constants, functions, and methods","id":"built-in-constants-functions-and-methods","depth":2},{"value":"Grammar reference","id":"grammar-reference","depth":2}],"frontmatter":{"seo":{"title":"Specification"}},"lastModified":"2026-10-26T18:28:28.485Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/custom-content/tsl/specification","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}