Program Structure

The shape of a J2 source file: comments, statements, blocks, and the words the language reserves for itself.

A program is a script

A J2 program is a sequence of statements executed top to bottom. There is no main function and no boilerplate; the file is the program.

greeting = "hello"
print(greeting)

Function and class definitions are statements like any other. Code at the top level can call a function defined earlier in the file, and functions may call functions defined later (mutual recursion works), but a top-level statement only sees bindings that were executed before it.

Comments

A comment starts with # and runs to the end of the line. There is no block comment form.

# This whole line is a comment.
x = 1   # so is the rest of this one
print(x)

Statements and newlines

Newlines separate statements; there are no semicolons. Inside parentheses, square brackets, or a map literal, lines join implicitly, so long calls and literals can wrap freely:

values = [
    10,
    20,
    30
]
print(sum(values))

Two statements may also share a line, separated by whitespace alone. This is idiomatic only for very small bodies:

x := 0
repeat x < 3 { print(x)  x += 1 }

Blocks

Braces group statements into a block wherever a body is expected: after if, around loop bodies, and as function bodies. A block used as a function body produces the value of its last expression (see Functions).

Identifiers

Names consist of letters, digits, and underscores, and cannot start with a digit. Convention is snake_case for functions and bindings and CapitalCase for classes. A leading underscore signals something internal by convention; the language does not enforce visibility. The bare underscore _ is special: it is the implicit element variable in filter predicates and a throwaway name in loops, not a general-purpose binding.

Keywords

The following words are reserved and cannot be used as names:

KeywordUsed for
funcfunction definitions and lambdas
givereturning a value from a function
for initeration
if elseconditionals, loop filters, and try handlers
repeat do loopcondition-driven and infinite loops
stop skipleaving a loop, or one iteration of it
untilbounding a for loop with a condition
tryerror handling
assertruntime checks
class extendsclass literals
and or notlogical operators
globaldeclaring a name at program scope; rarely needed, since top-level bindings are already visible to functions
rustthe raw native escape hatch, disabled by default (see How J2 Runs)

The literals true, false, and null and the numeric constants PI, E, TAU, INF, NAN, MAX_VAL, and MIN_VAL are also reserved; they are covered in Values and Types.

Diagnostics

J2 reports problems in two shapes. Errors caught before the program runs come with the offending line and a caret:

error[SyntaxError]: unexpected token in expression: Colon
  --> report.j2:14:9
   |
14 | m = {"a": 1}
   |         ^

Errors raised while the program runs print the error class, a message, and the line, and end the program with a nonzero exit status unless a try handles them (see Error Handling):

ZeroDivisionError: division by zero (at line 3)