Skip to main content

Lexical Elements

This page describes how the Atmos Automation Language reads source text: comments, names, keywords, string and number literals, and the indentation rules that group statements into blocks. The language is based on Starlark, so the syntax looks like Python, with a smaller set of tokens.

Source files are UTF-8 text. A program is a sequence of statements; see Statements for what each statement does.

Comments​

A comment starts with # and runs to the end of the line. Comments can follow code on the same line or occupy a line of their own.

# A full-line comment.
region = "us-east-1" # A trailing comment.

There are no block comments. A string written on its own line is an ordinary expression, so the Atmos Automation Language has no docstrings; use # comments to document functions.

Identifiers​

An identifier names a variable, function, parameter, or attribute. It starts with a letter or underscore and continues with letters, digits, and underscores. Identifiers are case-sensitive, so Region and region are different names. Non-ASCII letters are allowed.

service_name = "api"
_internal = 1
Replicas2 = 3

A name that starts with an underscore is private to its file: load() refuses to import it. See Statements.

Keywords​

These words have a fixed meaning and cannot be used as identifiers:

and, or, not
Boolean operators
in, not in
Membership tests
if, elif, else
Conditional statements and conditional expressions
for, while
Loops and comprehensions
break, continue, pass
Loop control and the empty statement
def, lambda, return
Function definitions
load
Import names from another file

Reserved words​

Python keywords with no equivalent in the language are reserved. Using one anywhere in a program is a syntax error, even where Python would accept it as a name:

as, async, await, class, del, except, finally, from, global, import, is, nonlocal, raise, try, with, yield

Each has a direct replacement:

Python habitUse instead
x is Nonex == None
del d[k]d.pop(k)
import jsonjson is predeclared; use load() for your own files
raise ValueError(msg)fail(msg)
try / exceptCheck conditions before acting; pass check=False to exec.run to inspect a failing command
global xCompute the value in a function and assign it once at the top level

The names True, False, and None are predeclared rather than keywords, and assert is an ordinary identifier with no special meaning.

Operators and delimiters​

The following tokens are recognized:

+ - * / // % ~ & | ^ << >>
== != < > <= >=
= += -= *= /= //= %= &= |= ^= <<= >>=
( ) [ ] { } , : ; .

Characters such as $, ?, and @ are not part of the language and are reported as unexpected input. There is no exponentiation operator; see Expressions and Operators. The ** token appears only in parameter lists and call arguments, where it collects or expands keyword arguments.

String literals​

A string literal is text between matching single quotes, double quotes, or triple quotes. Single-quoted and double-quoted strings are equivalent and must fit on one line. Triple-quoted strings (""" or ''') can span lines and can contain unescaped quotes.

a = "say \"hi\""
b = 'say "hi"'
c = """first line
second line with "quotes" and 'quotes'"""
print(a) # say "hi"
print(b) # say "hi"
print(c)
# first line
# second line with "quotes" and 'quotes'

A line break inside a single-quoted or double-quoted string is a syntax error. End a physical line with a backslash to continue the string on the next line without including a newline:

message = "deploying to \
production"
print(message) # deploying to production

Escape sequences​

Inside ordinary strings, a backslash starts an escape sequence:

\\
Backslash
\' and \"
Single and double quote
\a \b \f \n \r \t \v
Bell, backspace, form feed, newline, carriage return, tab, vertical tab
\ooo
Octal byte value, one to three digits, up to \177
\xhh
Hexadecimal byte value from \x00 to \x7f
\uXXXX
Unicode code point with four hex digits
\UXXXXXXXX
Unicode code point with eight hex digits
backslash, then newline
Line continuation; produces no character

An unrecognized escape such as \q is a syntax error, as are octal and hexadecimal escapes above the ASCII range (\xff, \377) and surrogate code points (\ud800). Write non-ASCII characters directly or with \u:

print("café") # café
print("tab:\there") # tab: here
print(len("\x41\101")) # 2

Raw strings​

Prefix a string with r to turn off escape processing. A backslash stands for itself, which suits regular expressions and Windows paths:

pattern = r"\d+\.\d+"
print(pattern) # \d+\.\d+
print(len(r"\n")) # 2
print(len("\n")) # 1

Raw strings can also use triple quotes. A raw string cannot end in an odd number of backslashes, because a backslash followed by the closing quote still escapes the quote.

Strings are text stored as UTF-8​

A string holds Unicode text encoded as UTF-8. Its len() counts bytes, and indexing and slicing work on bytes. Use .codepoints() to walk the characters of a string. See Data Types.

No implicit concatenation​

Adjacent string literals are not joined automatically. Join them with +, or wrap the pieces in a call to "".join([...]):

url = "https://" + "example.com" + "/status"
print(url) # https://example.com/status

Bytes literals​

Prefix a string with b to create a bytes value, a sequence of byte values. In a bytes literal, \xhh can express any byte from \x00 to \xff. The rb prefix gives a raw bytes literal.

magic = b"\x89PNG"
print(len(magic)) # 4
print(magic[1]) # P (a one-byte bytes value)
print(type(magic)) # bytes

See Data Types for what bytes values support.

Number literals​

Integers​

Integer literals can be written in four bases. Prefixes are case-insensitive:

FormExampleValue
Decimal255255
Hexadecimal0xFF, 0Xff255
Octal0o377, 0O377255
Binary0b11111111, 0B11111111255

Integers have arbitrary precision, so 99999999999999999999999999 is a valid literal. Leading zeros, as in 0777 or 08, are a syntax error: use the 0o prefix for octal. Underscore digit separators such as 1_000 are not accepted.

Floating-point numbers​

A float literal has a decimal point, an exponent, or both. Digits are optional on either side of the point:

print(1.5) # 1.5
print(.5) # 0.5
print(5.) # 5.0
print(1e3) # 1000.0
print(1.5E-2) # 0.015
print(0.1 + 0.2) # 0.30000000000000004

Floats are IEEE 754 double-precision numbers. A literal too large to represent, such as 1e400, is a syntax error. When printed, a float uses the shortest digits that read back as the same value, and switches to exponent notation for magnitudes of 1,000,000 and above or below 0.0001:

print(123456.0) # 123456.0
print(1234567.0) # 1.234567e+06
print(0.0001) # 0.0001
print(0.00001) # 1e-05

There are no complex-number or hexadecimal-float literals.

Indentation and blocks​

Indentation groups statements into blocks. A statement that ends in a colon (if, for, while, def) is followed by an indented block, and the block ends when the indentation returns to the outer level. Use four spaces per level.

def classify(replicas):
if replicas > 10:
return "large"
return "small"

Rules for indentation:

  • Every line in a block must use the same indentation, and a dedent must return to a level that an enclosing block already used. Otherwise the parser reports unindent does not match any outer indentation level.
  • A tab advances to the next multiple of eight columns. Mixing tabs and spaces within one file is legal but invites confusion, so use spaces.
  • Blank lines and comment-only lines do not affect indentation.
  • A block with a single simple statement can sit on the same line, as in if ready: print("go").
  • Unexpected extra indentation is a syntax error.

Line continuation and statement separators​

A statement normally ends at the end of the line. A statement continues onto the next line in two cases.

Inside parentheses, brackets, or braces, line breaks are ignored, and comments can appear after each element:

settings = {
"region": "us-east-1", # Primary region.
"tags": ["web", "api"],
}
total = (1 +
2 +
3)
print(total) # 6

Outside brackets, end a line with a backslash to continue it:

total = 1 + \
2
print(total) # 3

Separate several simple statements on one line with a semicolon. A trailing semicolon is allowed:

a = 1; b = 2
print(a + b) # 3

Next steps​