Syntax

In the following expr* means zero or more repetitions of the syntax form expr, while expr+ means one or more repetitions or expr and expr? means zero or one occurrences of expr and finally expr | expr' means either the form expr or expr'. For example an non-negative integer literal can be written as:

digit = '0' | ... | '9'
number = digit+

Likewise we define identifiers:

identifier_start = ('A' | ... | 'Z' | 'a' ... 'z')
identifier = identifier_start (identifier_start | '_' | digit)*

Musical notes

Ludwig has special syntax for expressing the value, pitch and octave of a musical note using the following syntax:

pitch =
  | 'A'
  | 'A#' | 'Bb'
  | 'B'
  | 'A'
  | 'C'
  | 'C#' | 'Db'
  | 'D'
  | 'D#' | 'Eb'
  | 'E'
  | 'F'
  | 'F#' | 'Gb'
  | 'G'
  | 'G#' | 'Ab'
  | '_'
  
value = (number '/')?

octave = ('/' number)?

note = value pitch octave

Values are expressed as their own inverse fractions. For example 8/C/4 denotes the Middle C note of value (i.e duration) 1/8. Underscore (i.e _) denotes a pause. A note’s value default’s to 8 while its octave defaults to 4. Hence C, 8/C, and C/4 are all parsed as 8/C/4.

Lists

The syntax for (cons) lists is shared by many other ML-family languages:

list = 
    | '[' ((expr ',')* expr)? ']'
    | expr ':' expr

Choice

Choice expressions are of the form lhs | rhs; Ludwig evaluates both lhs and rhs and keeps both results unless one of them fails to compute; in which case only the other one is left. Of course, if both options fail then the choice also fails.

choice = expr '|' expr

Unification

There plus = symbol in Ludwig doesn’t mean assignment but rather unification which you can think of as ‘structural equality’, with some caveats. Unification expressions always contain continuation expressions.

unify = expr '=' expr 'in' expr

Bindings

Because there is no explicit notion of assignment bindings look a bit different, though there is syntax sugar to alleviate this.

binding =
  | 'let'   identifier 'in' expr
  | 'let'   identifier '='  expr 'in' expr
  | 'const' identifier 'in' expr

The second form reduces to the first by putting a unification expression in its body. This looks like regular assignment but is quite different semantically.

Constants are defined by using the const keyword, see the next section for the difference between the two.

Lambdas

Lambdas (i.e anonymous function expressions) share the same syntax with Haskell-like programming languages:

lambda = '\' identifier '->' expr

Applications

Like you would expect function application is curried and is done by simply juxtaposition:

apply = expr expr

Summary

expr =
  | note
  | list
  | choice
  | unify
  | lambda
  | apply