Syntax
On this page
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