Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2. Grammar

This section defines the notation used for grammar fragments throughout the specification, and then gives the complete grammar. Fragments quoted in other sections are taken from the complete grammar verbatim.

2.1 Notation

A variant of Extended Backus–Naur Form:

FormMeaning
A = B ;Rule: A is defined as B.
"x"A terminal: the literal source text x.
UPPERCASEA token produced by the lexer (§1), e.g. NAME, INT, NEWLINE.
A , BA followed by B.
A | BEither A or B.
( … )Grouping.
[ A ]Zero or one A.
{ A }Zero or more A.

2.2 What the grammar does and does not say

The grammar is context-free and written for readability. Two things are specified elsewhere:

  • Operator precedence and associativity. The expression rule below is deliberately ambiguous (expression , binary_op , expression). The real precedence is the table in §4, implemented by a Pratt parser. Comparisons are non-associative — a < b < c is a syntax error.
  • Assignment targets. The grammar allows assignable = NAME | attribute | index, but the parser actually parses a full expression on the left of = and then checks that it is one of those three forms, so that a mistake like a + b = 1 gets a precise "cannot assign to this expression" message (E0104) rather than a generic parse failure.

2.3 Layout tokens in the grammar

NEWLINE, INDENT, and DEDENT appear in the grammar as ordinary terminals. They are produced by the lexer from indentation (§1.2, §1.4), so the grammar itself never mentions whitespace. Every block is exactly:

block = ":" , NEWLINE , INDENT , statement , { statement } , DEDENT ;

2.4 The complete grammar

This is the whole of Korrin's Milestone 1 syntax. It is kept in step with crates/korrin/src/parser/ and its tests.

Tokens

Produced by the lexer (§1):

NEWLINE   end of a logical line
INDENT    start of a more-indented block
DEDENT    end of an indented block
NAME      identifier that is not a keyword
INT       integer literal
FLOAT     float literal
STRING    string literal with no interpolation holes
EOF       end of input

An interpolated string (§1.7) is not one token — the lexer emits STRSTART ( STRTEXT | STREXPRSTART expression STREXPREND )* STREND.

plus the 19 keywords and the operator / punctuation tokens listed in §1.8.

Modules and blocks

module = { NEWLINE } , { statement } , EOF ;

block  = ":" , NEWLINE , INDENT , statement , { statement } , DEDENT ;

Statements

statement =
      simple_statement
    | if_statement
    | while_statement
    | for_statement
    | function_definition
    | class_definition ;

simple_statement =
    ( assignment | return_statement | "break" | "continue" | "pass" | expression ) ,
    NEWLINE ;

assignment       = assignable , "=" , expression ;
assignable       = NAME | attribute | index ;

return_statement = "return" , [ expression ] ;

if_statement =
    "if" , expression , block ,
    { "elif" , expression , block } ,
    [ "else" , block ] ;

while_statement = "while" , expression , block ;

for_statement   = "for" , NAME , "in" , expression , block ;

function_definition =
    "fn" , NAME , "(" , [ parameter_list ] , ")" , block ;
parameter_list = NAME , { "," , NAME } , [ "," ] ;

class_definition =
    "class" , NAME , [ "(" , NAME , ")" ] , ":" , NEWLINE ,
    INDENT , class_member , { class_member } , DEDENT ;
class_member = function_definition | ( "pass" , NEWLINE ) ;

Expressions

Written ambiguously; see §4 for precedence.

expression =
      literal
    | interpolated_string
    | NAME
    | "super" , "." , NAME
    | list_literal
    | map_literal
    | "(" , expression , ")"
    | unary_op , expression
    | expression , binary_op , expression
    | call
    | attribute
    | index ;

call          = expression , "(" , [ argument_list ] , ")" ;
argument_list = expression , { "," , expression } , [ "," ] ;
attribute     = expression , "." , NAME ;
index         = expression , "[" , expression , "]" ;

list_literal = "[" , [ expression , { "," , expression } , [ "," ] ] , "]" ;
map_literal  = "{" , [ map_entry , { "," , map_entry } , [ "," ] ] , "}" ;
map_entry    = expression , ":" , expression ;

interpolated_string =
    STRSTART , { STRTEXT | ( STREXPRSTART , expression , STREXPREND ) } , STREND ;

unary_op  = "-" | "not" ;
binary_op =
      "+" | "-" | "*" | "/" | "%"
    | "==" | "!=" | "<" | "<=" | ">" | ">="
    | "and" | "or" ;

literal = INT | FLOAT | STRING | "true" | "false" | "nil" ;