Nox

Language reference

Grammar

A compact grammar of Nox. It is descriptive, not normative — the parser in compiler/parser/ is the definition — and it omits the lexical details in Lexical structure. Notation: A B sequence, A | B choice, [A] optional, {A} zero or more, (…) grouping, quoted text is literal, NEWLINE, INDENT, DEDENT come from the lexer.

Program and statements#

Text
program        = { statement } ;
block          = NEWLINE INDENT statement { statement } DEDENT ;

statement      = simple_stmt NEWLINE | compound_stmt ;
simple_stmt    = var_decl | assignment | aug_assignment | tuple_assignment
               | expr
               | "pass" | "break" | "continue"
               | "return" [ expr { "," expr } ]
               | "raise" expr
               | "del" target
               | "assert" expr [ "," expr ]
               | "defer" call
               | import_stmt ;

var_decl       = NAME ":" type "=" expr ;
assignment     = target "=" expr ;
aug_assignment = target aug_op expr ;
aug_op         = "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**=" | "&=" | "|=" | "^=" | "<<=" | ">>=" ;
tuple_assignment = target "," target { "," target } "=" expr { "," expr } ;
target         = NAME | target "." NAME | target "[" expr "]" ;

import_stmt    = "import" dotted [ "as" NAME ]
               | "from" dotted "import" import_name { "," import_name } ;
import_name    = NAME [ "as" NAME ] ;
dotted         = NAME { "." NAME } ;

compound_stmt  = if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | lowlevel_stmt
               | func_def | class_def | protocol_def | extern_def ;

if_stmt        = "if" expr ":" block { "elif" expr ":" block } [ "else" ":" block ] ;
while_stmt     = "while" expr ":" block ;
for_stmt       = "for" for_target "in" expr ":" block ;
for_target     = NAME | NAME "," NAME ;
try_stmt       = "try" ":" block { except_clause } [ "finally" ":" block ] ;
except_clause  = "except" [ ( dotted | "(" dotted { "," dotted } ")" ) [ "as" NAME ] ] ":" block ;
with_stmt      = "with" expr [ "as" NAME ] ":" block ;
lowlevel_stmt  = "lowlevel" ":" block ;

Definitions#

Text
decorator      = "@" dotted [ "(" [ literal { "," literal } ] ")" ] NEWLINE ;

func_def       = { decorator } [ "async" ] "def" NAME [ type_params ] "(" [ params ] ")" "->" type ":" block ;
type_params    = "[" NAME { "," NAME } "]" ;
params         = param { "," param } ;
param          = "self" [ ":" type ]
               | NAME ":" type [ "=" default ] ;
default        = literal | "-" number | "None" ;

class_def      = { decorator } "class" NAME [ type_params ] [ "(" dotted ")" ] ":" NEWLINE INDENT
                 [ docstring ] { field_decl | func_def | "pass" } DEDENT ;
field_decl     = NAME ":" type NEWLINE ;

protocol_def   = "protocol" NAME ":" NEWLINE INDENT { method_sig } DEDENT ;
method_sig     = "def" NAME "(" [ params ] ")" "->" type ":" NEWLINE INDENT "pass" NEWLINE DEDENT ;

extern_def     = { decorator } "extern" "def" NAME "(" [ params ] ")" "->" type
                 "from" STRING [ "with_rt" ] [ "retains" "(" NAME { "," NAME } ")" ] ;

Types#

Text
type           = base_type [ "|" "None" ] ;
base_type      = NAME                                   (* int float bool str None, class, protocol, u8 … *)
               | NAME "[" type { "," type } "]"        (* list[T] dict[K,V] set[T] tuple[A,B] Task[T] Channel[T] ptr[T] Box[int] *)
               | "(" [ type { "," type } ] ")" "->" type  (* function type *)
               | dotted ;                               (* qualified class name: util.Box *)

Expressions#

Listed from loosest to tightest binding:

Text
expr           = lambda | conditional ;
lambda         = "lambda" [ NAME { "," NAME } ] ":" expr ;
conditional    = or_expr [ "if" or_expr "else" expr ] ;
or_expr        = and_expr { "or" and_expr } ;
and_expr       = not_expr { "and" not_expr } ;
not_expr       = "not" not_expr | comparison ;
comparison     = bit_or { comp_op bit_or } ;
comp_op        = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not" "in" | "is" | "is" "not" ;
bit_or         = bit_xor { "|" bit_xor } ;
bit_xor        = bit_and { "^" bit_and } ;
bit_and        = shift { "&" shift } ;
shift          = sum { ( "<<" | ">>" ) sum } ;
sum            = term { ( "+" | "-" ) term } ;
term           = factor { ( "*" | "/" | "//" | "%" ) factor } ;
factor         = ( "-" | "+" | "~" ) factor | power ;
power          = postfix [ "**" factor ] ;
postfix        = atom { "." NAME | "[" subscript "]" | "[" type_args "]" | "(" [ args ] ")" } ;
subscript      = expr | [ expr ] ":" [ expr ] [ ":" [ expr ] ] ;
args           = arg { "," arg } ;
arg            = expr | NAME "=" expr ;

atom           = NAME | NUMBER | STRING | FSTRING | "True" | "False" | "None"
               | "(" expr ")" | tuple | list | dict | set | comprehension
               | "spawn" call | "await" postfix ;
tuple          = "(" expr "," [ expr { "," expr } ] ")" ;
list           = "[" [ expr { "," expr } [ "," ] ] "]" ;
dict           = "{" [ expr ":" expr { "," expr ":" expr } ] "}" ;
set            = "{" expr { "," expr } "}" ;
comprehension  = "[" expr comp_for "]" | "{" expr ":" expr comp_for "}" | "{" expr comp_for "}" ;
comp_for       = ( "for" for_target "in" or_expr { "if" or_expr } )+ ;

A generator expression expr comp_for is also accepted as the single argument of a call.