Skip to content

Compiler Reference

The Gen compiler is written in Rust and transforms .gen source files into MusicXML.

Pipeline

Source → Lexer → Tokens → Parser → AST → Semantic Analysis → MusicXML
  1. Lexer tokenizes the input into notes, modifiers, and metadata
  2. Parser builds an Abstract Syntax Tree (AST)
  3. Semantic Analysis validates measure durations and structure
  4. MusicXML Generator outputs valid MusicXML 4.0

Public API

Basic Compilation

use gen::compile;
let source = r#"---
title: My Song
---
C D E F
G A B ^C
"#;
let musicxml = compile(source)?;

API Functions

FunctionDescription
compile(source)Full compilation with validation
compile_unchecked(source)Skip validation (for incomplete scores)
compile_with_options(source, clef, octave, transposition)Custom clef and transposition
compile_with_mod_points(source, clef, octave, group, key)Instrument-specific mod points
compile_to_tab(source)Guitar TAB MusicXML
compile_to_ascii_tab(source)ASCII tablature for terminal
compile_to_tab_data(source)Structured TAB data
compile_with_display_pitch(source, mode)Concert or transposed display

compile()

Standard compilation with full validation:

use gen::compile;
let musicxml = compile("C D E F")?;

Validates:

  • Measure durations match time signature
  • Repeat markers are matched
  • Endings follow correct structure

compile_unchecked()

Skip validation for incomplete or in-progress scores:

use gen::compile_unchecked;
// Incomplete measure (3 beats in 4/4)
let musicxml = compile_unchecked("C D E")?;

compile_with_options()

Custom clef, octave shift, and transposition:

use gen::{compile_with_options, Transposition};
// Bass clef, down one octave, for Bb instrument
let musicxml = compile_with_options(
"C D E F",
"bass", // clef: "treble", "bass", or "tab"
-1, // octave_shift: -2 to +2
Transposition::for_key("Bb")
)?;

compile_with_mod_points()

For instrument-specific octave adjustments:

use gen::compile_with_mod_points;
let source = "C D @Eb:^ E F";
// Compile for Eb alto sax
let musicxml = compile_with_mod_points(
source,
"treble",
0,
Some("eb"), // instrument_group: "eb", "bb", "f", or None
Some("Eb") // transpose_key: "C", "Bb", "Eb", "F"
)?;

compile_to_tab()

Guitar tablature as MusicXML:

use gen::compile_to_tab;
let musicxml = compile_to_tab("C D E F")?;
// Outputs MusicXML with TAB clef and string/fret notation

compile_to_ascii_tab()

ASCII tablature for terminal display:

use gen::compile_to_ascii_tab;
let tab = compile_to_ascii_tab("C D E F")?;
println!("{}", tab);
// e|--------|
// B|--------|
// G|--------|
// D|-----2--|
// A|--3-----|
// E|--------|

compile_to_tab_data()

Structured TAB data for programmatic access:

use gen::compile_to_tab_data;
let tab = compile_to_tab_data("C D E F")?;
for measure in &tab.measures {
for note in &measure.notes {
if let Some(pos) = note.position {
println!("String {}, Fret {}", pos.string, pos.fret);
}
}
}

compile_with_display_pitch()

Control concert vs. transposed display:

use gen::compile_with_display_pitch;
// Concert pitch (notes shown as they sound)
let musicxml = compile_with_display_pitch(source, "concert")?;
// Transposed (notes shown as player reads)
let musicxml = compile_with_display_pitch(source, "transposed")?;

Modules

lexer.rs

Tokenizes source into a stream of tokens:

TokenDescription
NoteNameNote letters A-G
RestRest indicator $
AccidentalSharp #, flat b, natural %
OctaveModifier^, ^^, _, __
RhythmModifier/, //, ///, p, o
DotDotted note *
TieTie -
SlurSlur ~
BracketOpen/CloseGroup brackets []
ParenOpen/CloseChord parentheses ()
PartDefPart definition @part:
PartContinueContinuation \\
KeyChangeKey change @key:
ModPointInstrument modifier @Eb:^

parser.rs

Converts tokens into AST. Handles:

  • YAML metadata extraction
  • Single-part and multi-part scores
  • Note grouping and tuplets
  • Repeat and ending structures

ast.rs

Core type definitions:

Score {
metadata: Metadata,
measures: Vec<Measure>, // Single-part
parts: Vec<Part>, // Multi-part
}
Metadata {
title: Option<String>,
composer: Option<String>,
time_signature: TimeSignature,
key_signature: KeySignature,
tempo: Option<Tempo>,
swing: Option<Swing>,
written_notation: WrittenNotation,
}
Measure {
elements: Vec<Element>,
repeat_start: bool,
repeat_end: bool,
ending: Option<Ending>,
is_pickup: bool,
}
Element {
Note { ... },
Rest { ... },
Chord { ... }, // Simultaneous notes
ChordSymbol { ... }, // Lead sheet annotation
}

semantic.rs

Validates the parsed score:

  • Measure duration: Total duration matches time signature
  • Pickup measures: Skipped for @pickup annotation
  • Multi-part alignment: All parts have same number of measures

musicxml.rs

Generates MusicXML 4.0 output:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN"
"http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="4.0">
<work><work-title>...</work-title></work>
<part-list>...</part-list>
<part id="P1">
<measure number="1">...</measure>
</part>
</score-partwise>

tab.rs

Guitar tablature generation:

  • 6-string standard tuning (E A D G B E)
  • Automatic fingering algorithm
  • Prefers open strings
  • Optimizes hand position

Duration System

Internal divisions: 840 per quarter note (LCM for tuplets)

DurationDivisions
Whole3360
Half1680
Quarter840
Eighth420
Sixteenth210
32nd105

Tuplet divisions calculated as: base_duration * normal_count / actual_count

Octave Mapping

GenMusicXML Octave
__2
_3
(none)4
^5
^^6

Middle C = octave 4 in both systems.

Error Types

pub enum GenError {
LexerError { message, line, column },
ParseError { message, line, column },
SemanticError { message, line },
MetadataError(String),
}

All errors include source location for debugging.

Transposition

Supported instrument keys:

KeySemitonesInstruments
C0Flute, Oboe, Trombone, etc.
Bb-2Trumpet, Clarinet, Tenor Sax
Eb-9Alto Sax, Baritone Sax
F-7French Horn

Building

Terminal window
cd packages/gen-compiler
cargo build --release

Testing

Terminal window
cargo test

Run specific test:

Terminal window
cargo test test_triplets