Dark mode

RK 1.2

Formal Grammar


This is the grammar implemented by the RK 1.2 interpreter. It is written in a conventional EBNF notation:

1. Lexical Grammar

  program_file     = { separator } , { procedure , { separator } } ,
                     program , { separator } , end_of_file ;

  identifier       = letter , { letter | digit | "_" } ;
  letter           = "A" .. "Z" | "a" .. "z" | "_" ;
  digit            = "0" .. "9" ;

  integer_literal  = digit , { digit } ;
  real_literal     = digit , { digit } , "." , digit , { digit } ,
                     [ exponent ]
                   | "." , digit , { digit } , [ exponent ] ;
  exponent         = ( "e" | "E" ) , [ "+" | "-" ] , digit , { digit } ;

  string_literal   = '"' , { string_character | escape } , '"' ;
  character_literal= "'" , ( character | escape ) , "'" ;
  escape           = "\\" , ( "n" | "t" | "r" | "\\" | "'" | '"' ) ;

  separator        = newline | ";" ;
  

Spaces, tabs, carriage returns and form feeds are whitespace. Comments begin with /* and end with */; they are lexical whitespace. A newline inside a comment still counts as a newline separator. Keywords are case-sensitive and are reserved:

  PROGRAM   FINISH    INTEGER   REAL      CHARACTER   STRING
  CONSTANT  SET       PRINT     ACCEPT    WHEN        OTHERWISE
  REPEAT    UNTIL     WHILE     FOR       FROM        TO
  STEP      BREAK     CONTINUE  CASE      OF          DEFAULT
  PROCEDURE RETURN    AND       OR        NOT         TRUE
  FALSE
  

Identifiers are case-sensitive. A keyword may not be used as an identifier. TRUE and FALSE are integer literals 1 and 0.

2. Program and Procedure Structure

  program          = "PROGRAM" , string_literal , block , "FINISH" ;

  procedure        = "PROCEDURE" , identifier , "[" ,
                     [ parameter_list ] , "]" , block ;

  parameter_list   = parameter , { "," , parameter } ;
  parameter        = type_name , identifier ;

  type_name        = "INTEGER" | "REAL" | "CHARACTER" | "STRING" ;

  block            = "{" , { separator } ,
                     [ statement , { separator , statement } , [ separator ] ] ,
                     "}" ;
  

Procedure declarations appear before program. The implementation permits at most 32 parameters and at most one program. A program file must end after FINISH except for whitespace, comments and separators. Constants and variables are declared by statements inside a block, not at file scope.

3. Statements

  statement        = declaration
                   | constant_declaration
                   | set_statement
                   | print_statement
                   | accept_statement
                   | when_statement
                   | repeat_statement
                   | while_statement
                   | for_statement
                   | case_statement
                   | break_statement
                   | continue_statement
                   | return_statement
                   | call_statement ;

  declaration      = type_name , identifier ,
                     [ "[" integer_literal "]" ] ,
                     [ ":=" , expression ] ;

  constant_declaration = "CONSTANT" , type_name , identifier ,
                     [ "[" integer_literal "]" ] ,
                     ":=" , expression ;

  set_statement    = "SET" , identifier , [ "[" expression "]" ] ,
                     "=" , expression ;

  print_statement  = "PRINT" , [ expression ,
                     { "," , expression } ] ;

  accept_statement = "ACCEPT" , identifier , [ "[" expression "]" ] ;

  when_statement   = "WHEN" , "[" , expression , "]" , block ,
                     [ { separator } , "OTHERWISE" , block ] ;

  repeat_statement = "REPEAT" , block , { separator } ,
                     "UNTIL" , "[" , expression , "]" ;

  while_statement  = "WHILE" , "[" , expression , "]" , block ;

  for_statement    = "FOR" , identifier , "FROM" , expression ,
                     "TO" , expression ,
                     [ "STEP" , expression ] , block ;

  case_statement   = "CASE" , expression , "{" , { separator } ,
                     { case_arm , { separator } } ,
                     [ "DEFAULT" , block , { separator } ] ,
                     "}" ;

  case_arm         = "OF" , case_value , { "," , case_value } , block ;

  case_value       = [ "-" ] , ( integer_literal | real_literal )
                   | character_literal | string_literal
                   | "TRUE" | "FALSE" ;

  break_statement  = "BREAK" ;

  continue_statement = "CONTINUE" ;

  return_statement = "RETURN" , [ expression ] ;

  call_statement   = identifier , "[" , [ argument_list ] , "]" ;
  

RETURN is accepted only inside a procedure. BREAK and CONTINUE are accepted only inside a loop. Separators are optional between a WHEN body and OTHERWISE. A complete WHEN, with or without OTHERWISE, requires a separator before a following statement. REPEAT is complete after its UNTIL condition, while WHILE, FOR and CASE are complete after their final closing brace; each requires a separator before a following statement. No separator is required immediately before the closing brace of the enclosing block.

An empty statement is permitted wherever a separator is permitted.

4. Expressions

  expression       = or_expression ;

  or_expression    = and_expression , { "OR" , and_expression } ;
  and_expression   = equality , { "AND" , equality } ;
  equality         = relational , { ( "=" | "!=" ) , relational } ;
  relational       = additive ,
                     { ( "<" | ">" | "<=" | ">=" ) , additive } ;
  additive         = multiplicative ,
                     { ( "+" | "-" ) , multiplicative } ;
  multiplicative   = unary ,
                     { ( "*" | "/" | "%" ) , unary } ;
  unary            = ( "-" | "NOT" ) , unary | primary ;

  primary          = integer_literal
                   | real_literal
                   | string_literal
                   | character_literal
                   | "TRUE"
                   | "FALSE"
                   | "(" , expression , ")"
                   | identifier , [ "[" , [ argument_list ] , "]" ] ;

  argument_list    = expression , { "," , expression } ;
  

All binary operators are left-associative within their levels. Unary - and NOT are right-associative. = is equality; assignment uses SET. A bracketed identifier is resolved at execution time as a procedure, an array subscript or a built-in function.

5. Brackets and Their Contexts

Square brackets are used only in the following positions:

  WHEN [E]                  condition        test E for truth
  WHILE [E]                 loop condition   test E before each iteration
  UNTIL [E]                 loop condition   stop when E is true
  PROCEDURE P[T V, ...]     declaration      parameter type and name
  P[A, B]                   expression/call  procedure or built-in call
  IDENTIFIER[N] after type  declaration      array size or string capacity
  A[E]                      expr/SET/ACCEPT  array subscript
  

For INTEGER, REAL and CHARACTER, the brackets in a declaration specify an array length. For STRING, they specify a capacity; they do not create an array of strings.

The same bracketed form NAME[...] is used for procedure calls, built-in calls and array subscripts. This is resolved when the expression is evaluated: a procedure of that name wins, then an array of that name, then a built-in function. A scalar variable does not hide a built-in.

6. Grammar Restrictions and Semantics

The context-free grammar above leaves several points to semantic rules:

7. Operator Precedence Summary

From tightest to loosest:

  primary        literals, names, calls, subscripts, ( ... )
  unary          -  NOT
  multiplicative *  /  %
  additive       +  -
  relational     <  >  <=  >=
  equality       =  !=
  conjunction    AND
  disjunction    OR
  

8. Parsing Example

  PROGRAM "LOOP" {
      INTEGER I

      FOR I FROM 1 TO 3 {
          PRINT I
      }
  }
  FINISH
  

Derivation outline:

  program
    PROGRAM "LOOP" block FINISH
    block
      declaration: INTEGER I
      for_statement: FOR I FROM 1 TO 3 block
        start: integer_literal 1
        limit: integer_literal 3
        print_statement: PRINT I