Chapter 5: A SPECIFICATION OF THE RUBATO LANGUAGE

This chapter attempts to define the Rubato language with respect to the major syntactic and semantic components of the language.

A EBNF grammar[21] for the language is presented in Appendix A. The grammar gives a reasonably precise syntactic definition of the language, although it is deliberately less precise and much more forgiving in the semantics of the language. If the grammar is used to implement a compiler for the language, the semantic analysis phase should weed out questionable constructs which are syntactically acceptable to the grammar.

This chapter will not attempt to describe the semantics of the language in a formal or rigorous manner, i.e. using the techniques of denotational semantics. An interesting follow-up to this thesis would be a rigorous denotational semantic specification of the Rubato language coupled with axiomatic and fixed point analysis of the semantics of programs written in the language.

5.1 Fundamental objects (types) of the language

The Rubato language has the following fundamental types or objects:

5.1.1 Number

A number is a whole (signed integer) value ranging from MININT to MAXINT(12). A number is not a true object in the language but more like a fundamental type. It cannot exist independently but must always be bound to an object. It is used as a component of the various other types in the language and is the type which is the result of an arithmetical expression within the language.

5.1.2 Note

Notes are the most basic building blocks of the language. A note corresponds to a musical note in a written score. In the Rubato language, a note is composed from its individual attributes. If the note is regarded as an event specification in a multidimensional perception space, then its attributes are the principal dimensions of the space.

The attributes of a note are:

pitch
a number corresponding to the frequency in which the note will sound in the audio spectrum. The relationship between the pitch number to the note frequency is determined by the synthesizer controlled by the Rubato system. If the target performance system is based on the MIDI specification, then the pitch number will range from 1 (lowest) to 127 (highest).
delay
the time interval between the note and the next note in sequence. This is given as a positive number from 0 to MAXPOS(13). This number is a multiple of a time interval known as a unit, which is relative to the current tempo or speed of the performance.
duration
the time interval in which the note will sound. This is a number similar to the delay value, i.e. a multiple of a unit.
velocity
the loudness or dynamic value of a note. On a performance system using MIDI, this is a number ranging from 1 to 127.
patch
the timbre of the note. This is a number ranging from 0 to 127 on MIDI systems.
channel
the output channel of the note. This is a number ranging from 0 to 15 on MIDI systems.
pressure
This is also related to the dynamics of the note and is usually related to the note's acoustic loudness. This is currently not implemented by the system.

Each note in a program written in the language will have all the above attributes, even if they are not specified explicitly by the user. Note attributes which are not specified will default to a value which depends on the lexical context of the note in the program.

5.1.3 Phrase

A phrase is a collection of block objects(14) which will be invoked sequentially. A phrase can contain object definitions which must occur in the beginning of the phrase, before the actual objects contained in the phrase.

5.1.4 Chord

A chord is a collection of block objects which will be invoked simultaneously or concurrently. A chord can also contain object definitions which must occur in the beginning of the chord, before the actual objects contained in the chord.

5.1.5 Envelope

An envelope is a specification of a sequence of linear functions to be applied in sequence to objects within a phrase. The specification of envelopes have not been finalised and envelopes are not currently implemented within the system.

5.1.6 Variable

A variable is an object that may hold a number. It is simply a read and write storage location which initially contains an undefined number. Once written to, future reads from the storage location will yield a number equivalent to the last number stored in the location. Variables may be used in the language wherever a number may be used.

5.1.7 Procedure

A procedure is either a phrase or chord which accept parameters(15) when invoked and pass these parameters onto the phrase or chord.

5.1.8 Function

A function is a procedure that returns a number back to the environment of the invoker. Functions also accept parameters when invoked and pass on these parameters to the phrase or chord. The number returned by a function can be used in the language wherever a number may be used.

5.1.9 Template

A template is a procedure with note parameters. It has an implicit phrase associated with it which is simply the phrase consisting of each note specified in the parameter in sequence from the first parameter of the template to the last parameter of the template.

5.1.10 Expression

An arithmetical expression is a group of numbers joined by either unary or binary arithmetical expression operators forming an expression tree when parsed. An expression has a result value which is equivalent to the number returned when the expression is evaluated. An expression may substitute for a number whenever one is required.

5.1.11 Statement

A statement is an object that may be contained within a phrase or a chord other than a chord or a phrase. There are many types of statements, including default statements, control flow statements, assignment statements, call statements which invoke a procedure or a template and other statements relating to music performance such as the tempo statement.

5.2 Lexical conventions

Programs written in the language are composed of tokens. Blanks, tabs, newlines, and comments (collectively, "white space") are ignored except as they serve to separate tokens. Some white space is required to separate otherwise adjacent identifiers, keywords and constants. If the input stream has been parsed into tokens up to a given charater, the next token is taken to include the longest string of characters which could possibly constitute a token. Upper case and lower case characters are considered significant when parsing a token.

The character sequences /* and */ introduce and terminate a comment, respectively. Comments do not nest.

The following classes of tokens are distinguished in the language:

5.2.1 Identifiers (Names)

An identifier is a sequence of letters and digits. The first character must be a letter. An identifier corresponds to a storage location either containing an object, i.e. a variable, or containing a pointer to an object, i.e. everything else. All characters in an identifier are considered significant.

5.2.2 Keywords

The following identifiers are reserved for use as keywords, and may not be used otherwise:

and             channel         chord           conductor               
default         delay           do              duration                
else            envelope        extern          function                
identifier      if              key             major           
measure         minor           mod             not             
note            or              patch           phrase          
pitch           pressure        procedure       repeat          
return          scale           template        tempo           
then            to              var             velocity                
while

Some reserved words are not currently implemented, but they are still recognized by the parser.

5.2.3 Constants

Constants are tokens returning a number. This can either be a sequence of digits 0 to 9, or an arithmetical expression of constant values which evaluate to a number.

5.2.4 Pitch Values

Pitch values are special constants returning a key. A key is a letter from the set of {A,B,C,D,E,F,G,a,b,c,d,e,f,g,r,R} together with a sequence of accidentals bound to the key. An accidental is one of

#
sharp
$
flat
%
natural

5.2.5 Special characters

Some special characters are accepted by the language in the same way as keywords are. These characters are given the following symbolic names.

{
BEGIN_PHRASE
}
END_PHRASE
[
BEGIN_CHORD
]
END_CHORD
(
BEGIN_PAREN
)
END_PAREN
=
ASSIGN
|
BAR
&
pitch
\
delay
;
velocity
!
duration
@
pressure
'
INCPIT
'
DECPIT
_
INCDUR
^
DECDUR
.
DELAYMUL
:
DELAYDIV
,
COMMA
==
EQUALS
<>
NOTEQUALS
<
LESS
>
GREATER
<=
LE
>=
GE
+
ADD
-
SUBTRACT
*
MULTIPLY
/
DIVIDE
~
UMINUS

5.3 Arithmetical expressions

These are usually written in infix notation, with precedence of operators enforced by a superset of algebraic precedence rules. The following binary operators are accepted in order of increasing precedence:

or
binary OR
and
binary AND
== and <>
Return 1 if operands are equal (not equal), 0 otherwise
<= and >=
Return 1 if the first operand is less than (greater than) or equal to the second operand, 0 otherwise
< and >
Return 1 if the first operand is less than (greater than) the second operand, 0 otherwise
+ and -
Integer addition (subtraction)
* and / and mod
Integer multiplication (division) (modulus or remainder)

In addition, the following unary operators are available, in order of increasing precedence:

not
Binary complement
~
unary minus
. and :
delaymul and delaydiv operators

The following may also be treated like a number in an arithmetical expression, these are collectively termed number equivalents.

variable
This is an identifier bound to a number
key factor
This is the keyword key followed by a pitch constant. It is numerically equal to the pitch number corresponding to the pitch constant.
current value
this is the default or nominal value of the attribute of a note. The current value is obtained by stuttering the attribute keyword, e.g. !! gives the current (default) value of the duration attribute.
function call
This is numerically equal to the value returned by invoking the function. A function call is specified by
function_identifier (parameter_list)

A parameter list is a list of numbers, notes, phrases, chords, templates, procedures, functions or envelopes separated by COMMA.

5.4 The Specification of A Note

A Note can be:

  1. A pitch constant
  2. The keyword pitch or the symbol & followed by an arithmetical expression.
  3. An identifier defined as a note followed by an arbitrary number of INCPIT and DECPIT operators.

An arbitrary number of attributes may be tacked on to the end of a note specification and forms part of the note. Attributes may be tacked on in any order and will be evaluated from left to right. In other words, if two attributes tacked on the note have the same type, the effect is either cumulative or the rightmost attribute will have precedence over the leftmost attribute.

Attributes are specified as either an attribute operator followed by an expression, an attribute operator with a relative expression, or an attribute operator with no trailing expressions. The following attributes are currently recognized:

delay or \
followed by an expression or relative expression
. or :
followed by a factor or a factor and a relative expression.
duration or !
followed by an expression or relative expression
^ or _
not followed by anything
velocity or ;
followed by an expression or relative expression
pressure or @
followed by an expression or relative expression
patch
followed by an expression
channel
followed by an expression

A relative expression is one of the following operators:

followed by an expression. An attribute specified with a relative expression can always be transformed semantically to an attribute specified with an expression by the following rule.

attribute rel_operator expression
can be transformed to
attribute current_value rel_operator ( expression )

For example, ;+20*3 is semantically equivalent to ;(;;+(20*3)).

DELAYMUL and DELAYDIV attribute specifications are special cases. A factor is a number or a number equivalent which is not specified as an arithmetical expression. A number equivalent is a key factor, an identifier bound to a number, the current value of an attribute, or the value returned by a function call. However, an arithmetical expression enclosed within parentheses is regarded as a factor. A DELAYMUL and DELAYDIV attribute specification can be semantically to a delay attribute specification.

. factor
is transformed to
delay ( current_value * factor )
and
: factor rel_operator expression
is transformed to
delay ( ( current_value / factor ) rel_operator expression )

5.5 Statements

The following types of statements are currently valid within a phrase or chord body. Notice that within this section, a statement is a term referring to either a statement, note, phrase, chord, or envelope.

5.5.1 if-then-else

if expression then statement1
if expression then statement1 else statement2

Evaluate expression. If result is nonzero, execute statement1, else execute statement2, if specified.

5.5.2 repeat-do

repeat expression do statement

Evaluate expression. If result is nonzero, execute statement iteratively the number of times specified by expression.

5.5.3 while-do

while expression do statement

Evaluate expression. If result is nonzero, execute statement and reevaluate expression repeatedly until expression evaluates to zero.

5.5.4 do-while

do statement while expression

Execute statement and evaluate expression repeatedly until expression evaluates to zero.

5.5.5 tempo

tempo expression1 = expression2

expression1 must evaluate to the number of units used as a counting quantity. expression2 evaluates to the number of times the counting quantity must occur in one minute.

5.5.6 default

default pitch1 = pitch2
default attribute

Changes the default value associated with note pitches or attributes. The first form of the statement implies a transposition of all notes up or down given by the offset of pitch2 relative to pitch1. The second form sets the default value of the note attribute to the value specified.

5.5.7 Assignment

identifier = object

Assigns (binds) object to identifier. Identifier assignments are 'strongly-typed', i.e. an object of a given type can only be assignmed to an identifier of the same type.

5.5.8 Template and Procedure Calls

identifier (parameter_list)

Invoke a template or procedure, depending on the type of the identifier. The syntax of a template or procedure call is deliberately made identical to the syntax of a function call.

5.5.9 return

return expression

Only valid within the context of a function body, this statement returns the value of the expression to the invoker of the function.

5.6 Identifiers and Definitions

Identifiers may be defined within the body of a phrase or a chord and 'typed' to an object type. Definitions must occur before the executable body of the phrase or chord. The definition of an identifier may also bind the identifier to an object. If a binding is not effected in the definition, the identifier can be bound later on within the phrase or chord through an assignment statement.

The syntax of an identifier definition is:

type identifier
type identifier = object
procedure identifier(parameter_defs) block
function identifier(parameter_defs) block
template identifier(template_defs)

A type is either a note, phrase, chord or envelope. parameter_defs is a list of definitions with no initial bindings separated by COMMA. A block is either a phrase or a chord. template_defs is a list of attribute lists separated by COMMA.

5.7 Scoping rules

The Rubato language employs static or lexical scoping rules on nested definitions. Definitions may be nested in the same way phrases or chords may be nested. Lexical scoping implies that the scope of an identifier is delimited by the phrase or chord that encloses the definition. The identifier is considered undefined outside the phrase or chord. Hence, the deeper the nesting of a phrase or chord, the more identifiers are defined within it (as it can access all its local variables plus variables belonging to its lexical environment). Conversely, a phrase or chord is not allowed to access variables belonging to phrases and chords nested within it. If an identifier declared within a phrase or a chord possesses the same name as an identifier declared outside the phrase or chord, the locally defined identifier hides or suspends the definition of the previous definition until the end of the phrase or chord.

5.7.1 Global definitions

Definitions may also occur outside the body of a phrase or chord, within the main source file. Such identifiers have global scope for the rest of the source file.

5.7.2 External definitions

External definitions are like global definitions, except that they are preceeded by the keyword extern. Also, they may not be initialized, i.e. bound to an object in the definition. External definitions also have global scope for the rest of the source file, but the actual definition of the (global) identifier lies within another source file. References to the same external or global identifier name are references to the same object.

5.8 conductor

conductor block

Every complete Rubato program (merger of the individual source files linked together) must have one conductor definition. It represents the phrase or chord that is executed by the Rubato machine when initially started up.

5.9 Compiler control lines or pragmas

These 'statements' control the action of the compiler when parsing the source file and are not actually part of the program proper.

5.9.1 $key

$key pitch major
$key pitch minor
$key accidental_list

This controls the key signature that notes coded in the file should be interpreted in. Currently, only major keys are implemented. The pitch specified may include accidentals.

The alternative $key specification takes an accidental_list, which is a list of pitches with accidentals separated by COMMA.

5.9.2 $default

$default pitch1 = pitch2

This controls the default octave or initial transposition of note pitches coded up for the rest of the language. It forces the compiler to transpose all notes coded up by an offset equal to the pitch number of pitch1 subtracted from the pitch number of pitch1. Uppercase pitches and lowercase pitches can be transposed independently, allowing two octaves to be readily accessible without the use of INCPIT or DECPIT operators.

5.10 Format of a source file

A source file in the language is simply a list of global or external definitions together with a conductor definition. Compiler control lines may intersperse the definitions.


Next: Chapter 6: A SPECIFICATION OF THE RUBATO MACHINE
Previous: Chapter 5: A SPECIFICATION OF THE RUBATO LANGUAGE
Back to: Table of Contents


Created on Sat Feb 21 20:21:24 1998 using a perl script called m2h from original troff mm document.
Click here to download a copy of m2h.

Author: Chris Tham
Email: Chris_Tham@hp.com