This is the grammar implemented by the RK 1.2 interpreter. It is written in a conventional EBNF notation:
{ X }means zero or more occurrences of X.[ X ]means an optional X.|separates alternatives.- Terminal words in uppercase, and quoted punctuation, are literal.
separatoris a newline or a semicolon.
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:
- A declaration may appear only as a statement; it is executed when reached.
- A name may be declared only once in the same block.
:=is legal only in a declaration, and not for an array.- A constant requires an initialiser and may not be an array.
- A constant may not be the target of SET, ACCEPT or a FOR loop.
- RETURN is legal only inside a procedure.
- BREAK and CONTINUE are legal only inside a loop.
- A FOR control variable must be an existing INTEGER or REAL scalar. For an INTEGER variable, start, limit and step must be integer or character values. The step may not be zero.
- CASE labels must be literals. A matching arm does not fall through. DEFAULT is optional and may appear once, after all OF arms.
- A procedure name may not be declared twice, and parameter names may not repeat within one procedure.
- A call must supply exactly the number of arguments declared by the procedure or required by the built-in.
- A procedure or PROGRAM name is a string literal, not an identifier.
- The main program has no parameters.
- OTHERWISE binds to the nearest WHEN.
- No GOTO or ELSE WHEN form exists.
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