Hi Johan (and list),

Last night our little girl kept me awake again, so i
took the oppertunity to teach her a bit about chords.

I've made a version-0 attempt at reading and typsetting
chord names, see input/test/chords.ly for an example.
Don't worry about the red-tape '@' and ';', just a
temporary hack.

The code that generates the chord names is rather crude
and broken, but it shows essentially how it can be done.
At each moment in time the Chord_name_engraver builds an 
array of pitches.  From this array a chord name must be
generated (tex(t)-only is the easiest, as in this example,
but it's trivial to make a nice class Chord_item, and
have the Chord_name_engraver use that.  Engravers and 
Items usually come in pairs, e.g. Beam_engraver/Beam etc.)

Have a look at the routine that generates the chord names,
perhaps you'ld like to have a go at perl->c++ translation?

    lily/chord-name-engraver.cc:
        [..]
        void
        Chord_name_engraver::do_process_requests ()

Of course, we should support different types of chord names,
perhaps switched using a property (chordNameStyle).  Or
even scheme code, some time later.

Have Fun,

Jan.

Generated by [EMAIL PROTECTED] using package-diff 0.62,
>From = lilypond-1.1.10, To = lilypond-1.1.10.jcn1

usage 

    cd lilypond-source-dir; patch -E -p1 < lilypond-1.1.10.jcn1.diff

Patches do not contain automatically generated files 
or (urg) empty directories, 
i.e., you should rerun autoconf, configure 
and possibly make outdirs.

--state
1.1.10
1.1.10.jcn1
++state
diff -urN ../lilypond-1.1.10/NEWS ./NEWS
--- ../lilypond-1.1.10/NEWS     Thu Dec  3 16:34:26 1998
+++ ./NEWS      Fri Dec  4 02:41:42 1998
@@ -1,3 +1,8 @@
+pl 10.jcn1
+       - input/test/chords.ly
+       - \type ChordNames and chord-name-engraver.*
+       - chords mode: \chords { <c e g> @c; @d7; }
+
 pl 10
 
 pl 9.jcn3
diff -urN ../lilypond-1.1.10/VERSION ./VERSION
--- ../lilypond-1.1.10/VERSION  Thu Dec  3 16:34:26 1998
+++ ./VERSION   Thu Dec  3 19:25:55 1998
@@ -2,7 +2,7 @@
 MAJOR_VERSION=1
 MINOR_VERSION=1
 PATCH_LEVEL=10
-MY_PATCH_LEVEL=
+MY_PATCH_LEVEL=jcn1
 
 # use the above to send patches: MY_PATCH_LEVEL is always empty for a
 # released version.
diff -urN ../lilypond-1.1.10/init/engraver.ly ./init/engraver.ly
--- ../lilypond-1.1.10/init/engraver.ly Thu Dec  3 16:34:26 1998
+++ ./init/engraver.ly  Fri Dec  4 02:08:02 1998
@@ -68,6 +68,7 @@
        \accepts "RhythmicStaff";
        \accepts "GrandStaff";
        \accepts "Lyrics";
+       \accepts "ChordNames";
 }
 
 
@@ -132,6 +133,7 @@
        \accepts "RhythmicStaff";
        \accepts "GrandStaff";
        \accepts "Lyrics";
+       \accepts "ChordNames";
 }
 
 \translator{
@@ -152,6 +154,21 @@
        \accepts "LyricVoice";
 }
 
+\translator{
+       \type "Line_group_engraver_group";
+
+       \name ChordNameVoice ;
+       \consists "Separating_line_group_engraver";
+       \consists "Chord_name_engraver";
+}
+
+\translator {
+       \type "Line_group_engraver_group";
+       \name ChordNames;
+       \consists "Vertical_align_engraver";
+       \accepts "ChordNameVoice";
+}
+
 ScoreContext = \translator {
        \type Score_engraver;
        \name Score;
@@ -169,6 +186,7 @@
        \accepts "Staff";
        \accepts "RhythmicStaff";       
        \accepts "Lyrics";
+       \accepts "ChordNames";
        \accepts "GrandStaff";
        \accepts "ChoirStaff";
 };
@@ -259,5 +277,6 @@
        \accepts "Staff";
        \accepts "RhythmicStaff";       
        \accepts "Lyrics";
+       \accepts "ChordNames";
        \accepts "GrandStaff";
 };
diff -urN ../lilypond-1.1.10/input/test/chords.ly ./input/test/chords.ly
--- ../lilypond-1.1.10/input/test/chords.ly     Thu Jan  1 01:00:00 1970
+++ ./input/test/chords.ly      Fri Dec  4 02:50:56 1998
@@ -0,0 +1,27 @@
+%{
+Would this be acceptable/good enough/convenient for entry?
+
+   Convention/Standard    Logical/Lily(?)
+   
+   C#                     cis
+   Cb                     ces
+   Cm/Cmin                c3-     
+   Caug                   c5+
+   Cdim                   c5-
+   Cmaj7                  c7
+   C7                     c7-
+   Csus/Csus4             c4^3
+%}
+
+scales = \notes\transpose c''\chords{
+               @c; @g; @d; @a; @e; @b; @fis;
+               @c; @f; @bes; @es; @as; @des; @ges;
+               @c6; @c7; @c9; @c11; @c13;
+       }
+
+\score{
+       <
+               \type ChordNames \scales
+               \type Staff \scales
+       >
+}
diff -urN ../lilypond-1.1.10/lily/chord-name-engraver.cc
./lily/chord-name-engraver.cc
--- ../lilypond-1.1.10/lily/chord-name-engraver.cc      Thu Jan  1
01:00:00 1970
+++ ./lily/chord-name-engraver.cc       Fri Dec  4 02:52:40 1998
@@ -0,0 +1,92 @@
+/*
+  chord-name-engraver.cc -- implement Chord_name_engraver
+
+  source file of the GNU LilyPond music typesetter
+
+  (c) 1998 Jan Nieuwenhuizen <[EMAIL PROTECTED]>
+*/
+
+#include "chord-name-engraver.hh"
+#include "musical-request.hh"
+#include "text-item.hh"
+#include "paper-def.hh"
+#include "lookup.hh"
+#include "paper-def.hh"
+#include "main.hh"
+#include "dimensions.hh"
+
+ADD_THIS_TRANSLATOR (Chord_name_engraver);
+
+Chord_name_engraver::Chord_name_engraver ()
+{
+}
+
+void
+Chord_name_engraver::acknowledge_element (Score_element_info i)
+{
+  if (Note_req* n = dynamic_cast<Note_req*> (i.req_l_))
+    pitch_arr_.push (n->pitch_);
+}
+
+bool
+Chord_name_engraver::do_try_music (Music* m)
+{
+  if (Note_req* n = dynamic_cast<Note_req*> (m))
+    {
+      pitch_arr_.push (n->pitch_);
+      return true;
+    }
+  return false;
+}
+
+void
+Chord_name_engraver::do_process_requests ()
+{
+  if (text_p_arr_.size ())
+    return;
+  if (!pitch_arr_.size ())
+    return;
+
+  Scalar style = get_property ("textstyle");
+  Scalar alignment = get_property ("textalignment");
+  Text_def* text_p = new Text_def;
+  text_p->align_dir_ = LEFT;
+  if (style.length_i ())
+    text_p->style_str_ = style;
+  if (alignment.isnum_b())
+    text_p->align_dir_= (Direction)(int)alignment;
+
+  Musical_pitch tonic = pitch_arr_[0];
+  text_p->text_str_ = tonic.str ();
+  for (int i=1; i < pitch_arr_.size (); i++)
+    {
+      Musical_pitch p = pitch_arr_[i];
+      int trap = (p.notename_i_ - tonic.notename_i_ + 8) % 8 + 1;
+      if (((trap != 3) && (trap != 5)) || (p.accidental_i_))
+        {
+         text_p->text_str_ += to_str ((p.notename_i_ -
tonic.notename_i_ + 8) % 8 + 1);
+         if (p.accidental_i_)
+           text_p->text_str_ += p.accidental_i_ < 0 ? "-" : "+";
+         if (i + 1 < pitch_arr_.size ())
+           text_p->text_str_ += "/";
+       }
+    }
+
+  Text_item* item_p =  new Text_item (text_p);
+  item_p->dir_ = DOWN;
+  item_p->fat_b_ = true;
+  text_p_arr_.push (item_p);
+  announce_element (Score_element_info (item_p, 0));
+}
+
+void
+Chord_name_engraver::do_pre_move_processing ()
+{
+  for (int i=0; i < text_p_arr_.size (); i++)
+    {
+      typeset_element (text_p_arr_[i]);
+    }
+  text_p_arr_.clear ();
+  pitch_arr_.clear ();
+}
+
diff -urN ../lilypond-1.1.10/lily/include/chord-name-engraver.hh
./lily/include/chord-name-engraver.hh
--- ../lilypond-1.1.10/lily/include/chord-name-engraver.hh      Thu Jan
1 01:00:00 1970
+++ ./lily/include/chord-name-engraver.hh       Fri Dec  4 02:47:39 1998
@@ -0,0 +1,35 @@
+/*
+  chord-name-engraver.hh -- declare Chord_name_engraver
+
+  source file of the GNU LilyPond music typesetter
+
+  (c) 1998 Jan Nieuwenhuizen <[EMAIL PROTECTED]>
+*/
+
+#ifndef CHORD_NAME_ENGRAVER_HH
+#define CHORD_NAME_ENGRAVER_HH
+
+#include "engraver.hh"
+#include "array.hh"
+#include "musical-pitch.hh"
+
+#include "lily-proto.hh"
+
+class Chord_name_engraver : public Engraver 
+{
+protected:
+  virtual void do_pre_move_processing ();
+  virtual void acknowledge_element (Score_element_info i);
+  virtual void do_process_requests ();
+  virtual bool do_try_music (Music* m);
+
+public:
+  Chord_name_engraver ();
+  VIRTUAL_COPY_CONS (Translator);
+
+private:
+  Array<Musical_pitch> pitch_arr_;
+  Link_array<Text_item> text_p_arr_;
+};
+
+#endif // CHORD_NAME_ENGRAVER_HH
diff -urN ../lilypond-1.1.10/lily/include/lily-proto.hh
./lily/include/lily-proto.hh
--- ../lilypond-1.1.10/lily/include/lily-proto.hh       Fri Nov 27
14:27:38 1998
+++ ./lily/include/lily-proto.hh        Fri Dec  4 02:30:25 1998
@@ -46,7 +46,7 @@
 struct Cadenza_req;
 struct Change_iterator;
 struct Change_translator;
-struct Simultaneous_music;
+struct Chord_name_engraver;
 struct Clef_change_req;
 struct Clef_item;
 struct Clef_engraver;
@@ -186,7 +186,6 @@
 struct Rhythmic_head;
 struct Rhythmic_grouping_req;
 struct Rhythmic_req;
-struct Single_malt_grouping_item;
 struct Scope;
 struct Separating_group_spanner;
 struct Score;
@@ -200,6 +199,8 @@
 struct Script_engraver;
 struct Script_req;
 struct Simple_music;
+struct Simultaneous_music;
+struct Single_malt_grouping_item;
 struct Skip_req;
 struct Slur;
 struct Slur_engraver;
diff -urN ../lilypond-1.1.10/lily/include/lyric-engraver.hh
./lily/include/lyric-engraver.hh
--- ../lilypond-1.1.10/lily/include/lyric-engraver.hh   Fri Nov 27
14:27:38 1998
+++ ./lily/include/lyric-engraver.hh    Fri Dec  4 02:28:19 1998
@@ -20,7 +20,6 @@
   virtual void do_pre_move_processing();
   virtual bool do_try_music (Music*);
   virtual void do_process_requests();
-  virtual void do_post_move_processing();
 
 public:
   Lyric_engraver();
diff -urN ../lilypond-1.1.10/lily/include/my-lily-lexer.hh
./lily/include/my-lily-lexer.hh
--- ../lilypond-1.1.10/lily/include/my-lily-lexer.hh    Mon Oct 12
14:10:55 1998
+++ ./lily/include/my-lily-lexer.hh     Thu Dec  3 18:43:47 1998
@@ -50,6 +50,7 @@
   Identifier*lookup_identifier (String s);
   Musical_pitch lookup_pitch (String s);
   void push_note_state();
+  void push_chord_state();
   void push_lyric_state();
   void pop_state();
   void LexerError (char const *);
@@ -60,6 +61,7 @@
   void print_declarations (bool init_b) const;
   void add_notename (String, Musical_pitch);
   bool note_state_b() const;
+  bool chord_state_b() const;
   bool lyric_state_b() const;
 };
 
diff -urN ../lilypond-1.1.10/lily/include/my-lily-parser.hh
./lily/include/my-lily-parser.hh
--- ../lilypond-1.1.10/lily/include/my-lily-parser.hh   Mon Nov 16
23:53:57 1998
+++ ./lily/include/my-lily-parser.hh    Thu Dec  3 23:18:23 1998
@@ -27,6 +27,7 @@
   void add_requests (Simultaneous_music*v);
 
   Simultaneous_music * get_note_element (Note_req * ,Duration *);
+  Simultaneous_music * get_chord (Musical_pitch, Array<Musical_pitch>*,
Array<Musical_pitch>*, Duration);
   Simultaneous_music* get_rest_element (String, Duration *);
   Simultaneous_music* get_word_element (String, Duration*);
   Melodic_req* get_melodic_req (Melodic_req* melodic, int quotes);
diff -urN ../lilypond-1.1.10/lily/lexer.ll ./lily/lexer.ll
--- ../lilypond-1.1.10/lily/lexer.ll    Mon Nov 16 23:53:57 1998
+++ ./lily/lexer.ll     Thu Dec  3 23:35:13 1998
@@ -62,6 +62,7 @@
 %option never-interactive 
 %option warn
 
+%x chords
 %x incl
 %x lyrics
 %x notes
@@ -102,7 +103,7 @@
        // windows-suck-suck-suck
 }
 
-<notes,incl,INITIAL,lyrics>{
+<INITIAL,chords,incl,lyrics,notes>{
   "%{" {
        yy_push_state (longcomment);
   }
@@ -136,11 +137,11 @@
 }
 
 
-<notes,INITIAL,lyrics>\\maininput           {
+<INITIAL,chords,lyrics,notes>\\maininput           {
        start_main_input ();
 }
 
-<notes,INITIAL,lyrics>\\include           {
+<INITIAL,chords,lyrics,notes>\\include           {
        yy_push_state (incl);
 }
 <incl>\"[^"]*\";?   { /* got the include file name */
@@ -184,29 +185,27 @@
 <notes>R               {
        return MEASURES;
 }
-<INITIAL,lyrics,notes>\\\${BLACK}*{WHITE}      {
+<INITIAL,chords,lyrics,notes>\\\${BLACK}*{WHITE}       {
        String s=YYText () + 2;
        s=s.left_str (s.length_i () - 1);
        return scan_escaped_word (s); 
 }
-<INITIAL,lyrics,notes>\${BLACK}*{WHITE}                {
+<INITIAL,chords,lyrics,notes>\${BLACK}*{WHITE}         {
        String s=YYText () + 1;
        s=s.left_str (s.length_i () - 1);
        return scan_bare_word (s);
 }
-<INITIAL,lyrics,notes>\\\${BLACK}*             { // backup rule
+<INITIAL,chords,lyrics,notes>\\\${BLACK}*              { // backup rule
        cerr << _ ("white expected") << endl;
        exit (1);
 }
-<INITIAL,lyrics,notes>\${BLACK}*               { // backup rule
+<INITIAL,chords,lyrics,notes>\${BLACK}*                { // backup rule
        cerr << _ ("white expected") << endl;
        exit (1);
 }
 <notes>{
-
        {ALPHAWORD}     {
                return scan_bare_word (YYText ());
-
        }
 
        {NOTECOMMAND}   {
@@ -249,7 +248,6 @@
 }
 
 <lyrics>{
-
        \" {
                start_quote ();
        }
@@ -281,6 +279,18 @@
                return yylval.c = YYText ()[0];
        }
 }
+<chords>{
+       {ALPHAWORD}     {
+               return scan_bare_word (YYText ());
+       }
+       {UNSIGNED}              {
+               yylval.i = String_convert::dec2_i (String (YYText ()));
+               return UNSIGNED;
+       }
+       . {
+               return yylval.c = YYText ()[0];
+       }
+}
 
 <<EOF>> {
        DOUT << "<<eof>>";
@@ -364,10 +374,17 @@
 }
 
 void
+My_lily_lexer::push_chord_state ()
+{
+       yy_push_state (chords);
+}
+
+void
 My_lily_lexer::push_lyric_state ()
 {
        yy_push_state (lyrics);
 }
+
 void
 My_lily_lexer::pop_state ()
 {
@@ -389,7 +406,7 @@
                yylval.id = id;
                return id->token_code_i_;
        }
-       if (YYSTATE != notes) {
+       if ((YYSTATE != notes) && (YYSTATE != chords)) {
                if (notename_b (str))
                        {
                        yylval.pitch = new Musical_pitch (lookup_pitch
(str));
@@ -412,7 +429,7 @@
 My_lily_lexer::scan_bare_word (String str)
 {
        DOUT << "word: `" << str<< "'\n";       
-       if (YYSTATE == notes){
+       if ((YYSTATE == notes) || (YYSTATE == chords)) {
                if (notename_b (str)) {
                    DOUT << "(notename)\n";
                    yylval.pitch = new Musical_pitch (lookup_pitch
(str));
@@ -430,6 +447,12 @@
 My_lily_lexer::note_state_b () const
 {
        return YY_START == notes;
+}
+
+bool
+My_lily_lexer::chord_state_b () const
+{
+       return YY_START == chords;
 }
 
 bool
diff -urN ../lilypond-1.1.10/lily/lyric-engraver.cc
./lily/lyric-engraver.cc
--- ../lilypond-1.1.10/lily/lyric-engraver.cc   Fri Nov 27 14:27:38 1998
+++ ./lily/lyric-engraver.cc    Fri Dec  4 02:21:35 1998
@@ -66,11 +66,6 @@
 }
 
 void
-Lyric_engraver::do_post_move_processing()
-{
-}
-
-void
 Lyric_engraver::do_pre_move_processing()
 {
   for (int i=0; i < text_p_arr_.size (); i++)
diff -urN ../lilypond-1.1.10/lily/my-lily-lexer.cc
./lily/my-lily-lexer.cc
--- ../lilypond-1.1.10/lily/my-lily-lexer.cc    Thu Dec  3 16:34:26 1998
+++ ./lily/my-lily-lexer.cc     Fri Dec  4 02:34:56 1998
@@ -28,6 +28,7 @@
   {"alternative", ALTERNATIVE},
   {"bar", BAR},
   {"cadenza", CADENZA},
+  {"chords", CHORDS},
   {"clef", CLEF},
   {"cm", CM_T},
   {"consists", CONSISTS},
diff -urN ../lilypond-1.1.10/lily/my-lily-parser.cc
./lily/my-lily-parser.cc
--- ../lilypond-1.1.10/lily/my-lily-parser.cc   Mon Nov 16 23:53:57 1998
+++ ./lily/my-lily-parser.cc    Fri Dec  4 01:59:22 1998
@@ -158,6 +158,108 @@
 }
 
 Simultaneous_music *
+My_lily_parser::get_chord (Musical_pitch tonic, Array<Musical_pitch>*
add_arr_p, Array<Musical_pitch>* sub_arr_p, Duration d)
+{
+  Simultaneous_music*v = new Request_chord;
+  v->set_spot (here_input ());
+
+  Note_req* n = new Note_req;
+  n->pitch_ = tonic;
+  n->duration_ = d;
+  v->add_music (n);
+
+  for (int i = 0; i < add_arr_p->size (); i++)
+    {
+      Musical_pitch p = tonic;
+      p.transpose ((*add_arr_p)[i]);
+      (*add_arr_p)[i] = p;
+    }
+  add_arr_p->sort (Musical_pitch::compare);
+  for (int i = 0; i < sub_arr_p->size (); i++)
+    {
+      Musical_pitch p = tonic;
+      p.transpose ((*sub_arr_p)[i]);
+      (*sub_arr_p)[i] = p;
+    }
+  sub_arr_p->sort (Musical_pitch::compare);
+
+  Musical_pitch third;
+  third.notename_i_ = 2;
+
+  Musical_pitch mthird;
+  mthird.notename_i_ = 2;
+  mthird.accidental_i_ = -1;
+
+  Musical_pitch missing;
+  missing = tonic;
+  missing.transpose (third);
+
+  Musical_pitch p;
+  p = tonic;
+  p.transpose (third);
+  p.transpose (mthird);
+
+  /*
+   must have minimum at 5 (3 is added automatically as missing)
+   */
+  if (!add_arr_p->size () 
+    || ((add_arr_p->size () == 1) && 
+         ((add_arr_p->top ().notename_i_ != p.notename_i_)
+           || (add_arr_p->top () < p))))
+    add_arr_p->push (p);
+
+  Array<Musical_pitch> triads;
+  triads.push (third);   // c e 
+  triads.push (mthird);  // d f 
+  triads.push (mthird);  // e g 
+  triads.push (third);   // f a 
+  triads.push (third);   // g b 
+  triads.push (mthird);  // a c 
+  triads.push (mthird);  // b d 
+
+  /*
+   add missing triads
+   */
+  for (int i = 0; i < add_arr_p->size (); i++)
+    {
+      Musical_pitch p = (*add_arr_p)[i];
+      if ((p > missing) && (p.notename_i_ != missing.notename_i_))
+        while ((p > missing) && (p.notename_i_ != missing.notename_i_))
+         {
+           add_arr_p->insert (missing, i++);
+           missing.transpose (triads[(missing.notename_i_ -
tonic.notename_i_ + 8) % 8]);
+         }
+       else
+         i++;
+    }
+
+  /*
+   add all that aren't subtracted
+   */
+  for (int i = 0; i < add_arr_p->size (); i++)
+    {
+      Musical_pitch p = (*add_arr_p)[i];
+      Note_req* n = new Note_req;
+      n->pitch_ = p;
+      n->duration_ = d;
+      for (int j = 0; j < sub_arr_p->size (); j++)
+        {
+         if (p == (*sub_arr_p)[j])
+           {
+             delete n;
+             n = 0;
+             break;
+           }
+       }
+      if (n)
+       v->add_music (n);
+    }
+
+  v->set_spot (here_input ());
+  return v;
+}
+
+Simultaneous_music *
 My_lily_parser::get_note_element (Note_req *rq, Duration * duration_p)
 {
   Simultaneous_music*v = new Request_chord;
diff -urN ../lilypond-1.1.10/lily/parser.yy ./lily/parser.yy
--- ../lilypond-1.1.10/lily/parser.yy   Thu Dec  3 16:34:26 1998
+++ ./lily/parser.yy    Fri Dec  4 01:14:03 1998
@@ -163,6 +163,7 @@
 %token BAR
 %token BEAMPLET
 %token CADENZA
+%token CHORDS
 %token CLEF
 %token CM_T
 %token CONSISTS
@@ -264,6 +265,9 @@
 %type <pitch>   explicit_musical_pitch steno_musical_pitch
musical_pitch absolute_musical_pitch
 %type <notereq>        steno_notepitch
 %type <pitch_arr>      pitch_list
+%type <music>  chord
+%type <pitch_arr>       chord_additions chord_subtractions
+%type <pitch>           chord_note
 %type <midi>   midi_block midi_body
 %type <duration>       duration_length
 
@@ -807,7 +811,13 @@
                { $$ = $3;
                  THIS->lexer_p_->pop_state ();
                }
-
+       | CHORDS
+               { THIS->lexer_p_->push_chord_state (); }
+       Music
+               {
+                 $$ = $3;
+                 THIS->lexer_p_->pop_state ();
+       }
        | LYRICS
                { THIS->lexer_p_->push_lyric_state (); }
        Music
@@ -1400,7 +1410,8 @@
 
 simple_element:
        steno_notepitch notemode_duration  {
-               if (!THIS->lexer_p_->note_state_b ())
+               if (!THIS->lexer_p_->note_state_b ()
+                 && !THIS->lexer_p_->chord_state_b ())
                        THIS->parser_error (_ ("have to be in Note mode
for notes"));
                $1->duration_ = *$2;
                $$ = THIS->get_note_element ($1, $2);
@@ -1425,8 +1436,61 @@
                $$ = THIS->get_word_element (*$1, $2);
                delete $1;
        }
+       | '@' chord ';' {
+               if (!THIS->lexer_p_->chord_state_b ())
+                       THIS->parser_error (_ ("have to be in Chord mode
for chords"));
+               $$ = $2;
+       }
+       ;
+
+
+chord:
+        NOTENAME_PITCH chord_additions chord_subtractions {
+               Duration d;
+               d.durlog_i_ = 0;
+                $$ = THIS->get_chord (*$1, $2, $3, d);
+        };
+
+chord_additions:
+       {
+               $$ = new Array<Musical_pitch>;
+       } 
+       | chord_additions chord_note {
+               $1->push (*$2);
+               $$ = $1;
+       }
        ;
 
+chord_note:
+        UNSIGNED {
+               $$ = new Musical_pitch;
+               $$->notename_i_ = ($1 - 1) % 8;
+               $$->octave_i_ = $1 > 7 ? 1 : 0;
+               $$->accidental_i_ = 0;
+        } 
+       | UNSIGNED '+' {
+               $$ = new Musical_pitch;
+               $$->notename_i_ = ($1 - 1) % 8;
+               $$->octave_i_ = $1 > 7 ? 1 : 0;
+               $$->accidental_i_ = 1;
+       }
+       | UNSIGNED '-' {
+               $$ = new Musical_pitch;
+               $$->notename_i_ = ($1 - 1) % 8;
+               $$->octave_i_ = $1 > 7 ? 1 : 0;
+               $$->accidental_i_ = -1;
+       }
+        ;
+
+chord_subtractions:
+       {
+               $$ = new Array<Musical_pitch>;
+       }
+       | '^' chord_subtractions chord_note {
+               $2->push (*$3);
+               $$ = $2;
+       }
+       ;
 
 /*
        UTILITIES

Reply via email to