denemo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Denemo-devel] status report


From: Roy Rankin
Subject: Re: [Denemo-devel] status report
Date: Wed, 23 Jul 2008 06:59:14 +1000
User-agent: Thunderbird 2.0.0.14 (X11/20080501)

Richard,

I am happy for you to check the changes in.

Note the the change in voice name breaks examples/SeveralVerses.denemo. I have not figured out how to get to the lyrics section of thos file.

Roy

Richard Shann wrote:
Thanks for the files - I would like to check them in, if it is all right
with you. I have a couple of additions: protection against a crash when the parse
does not work, and parsing LilyPond directives. And, of course,
restoration of the .ly load option. I think this is plenty for the release, so if I don't hear back, I'll
get this checked in and we can go.
Thanks very much for this great piece of work.
Richard



On Tue, 2008-07-22 at 23:02 +1000, Roy Rankin wrote:
Richard,

Here are my files and a quick summary of the changes
exportlilypond.c
1> always write /context to help parsing and supply voice name
2> voice name - name-movement rather than movement-name to aid parsing of name
3> midi instruments again output

lyparser.y
1> do not generate error for case score_body lilypond_header
2> be more specific for some parsing error
3> clear some pointers for score - note your suggestions caused troubles for me

lyparserfuncs.c
1> rewrite break_into_measure to handle recursive identifier
2> generate_chords_and_sequentials handles identifiers
3> name of first voice is set (old bug)
4> added some debugging

This code is working for my simple tests of both old and new versions of Denemo. I think this gives about the same level of functionality as the lilypond read on the old version, but many valid syntaxes that can be handled by Denemo fail.

regards,
Roy Rankin

Richard Shann wrote:
On Tue, 2008-07-22 at 09:01 +1000, Roy Rankin wrote:
I am currently dumping and reading back simple LilyPond files from Denemo. However, I still have a few issues to cleanup, but if I do not manage to get this done tonight, my next chunk of free time will be early next week.
I have also been getting a chance to look over this, and I think I
understand the code as at present.
Could you just email me the lyparser.* lyparserfuncs.* files that you
have, so that I can understand where you have got to? (Don't email them
to the list, as it blocks (large?) attachemts, send them direct or post
them up somewhere).
I would like to read over what you have done, to see how it fits in to
my understanding of the code. It doesn't need to be compilable or
anything, just readable.

 As the code no longer needs to keep the input LilyPond as strings for
output (which was the purpose of the old code) I think it can be
simplified and made more able to cope with LilyPond from a variety of
sources.
In the slightly longer term, I hope be able to select some LilyPond text
in a foreign text file (viz. a block of music) and paste it directly
into Denemo, using the lyparser (essentially, by surrounding it with
\score{ .... } and then parsing it in to a clipboard score).

Best regards,

Richard

Of course at this point the code is most likely not bullet proof.

Regards,
Roy Rankin


Richard Shann wrote:
On Sun, 2008-07-20 at 22:15 -0500, Jeremiah Benham wrote:
What is left on the TODO list for the release?
Well, no one has actually said that they think the release should
contain a fix for LilyPond import, but Roy is keen that it should be
possible to load back simple Denemo files saved in LilyPond format, and
I am keen that it should load some LilyPond files (for which fixes are
available). I have been getting to grips with the LilyPond load for this
purpose, but I can't say quite how long it may take.
It is one of the curses of projects like this that no one says they need
something until we announce a release, and then it drags on and on. I
think we should set deadlines for feature requests, announce a schedule
and, barring showstopping bugs, stick to it.

I do not want to make any
changes to importMidi until the next release. I think I will check out
the docs and see what I update. When we make a release it may be
embarrassing if the docs are behind. I will look over them and have them
update sometime tomorrow.
That sounds good, the Print Excerpt has no documentation, I think. There
is an email from you which I didn't respond to (sorry) about this: the
situation is that if you have a selection, you probably want that as the
excerpt, or you may want the whole piece of music as a .png (which is
only properly supported if it fits on one page, else the user has to dig
the rest out from .denemo/denemoprint*<n>*.png for n pages), (or even to
select measures by start measure number/end measure number). The
interface is supposed to support all this, (but is a bit cheap and
cheerful?).

Richard





_______________________________________________
Denemo-devel mailing list
address@hidden
http://lists.gnu.org/mailman/listinfo/denemo-devel

_______________________________________________
Denemo-devel mailing list
address@hidden
http://lists.gnu.org/mailman/listinfo/denemo-devel


_______________________________________________
Denemo-devel mailing list
address@hidden
http://lists.gnu.org/mailman/listinfo/denemo-devel

plain text document attachment (lyparser.y)
%{ // -*-Fundamental-*-

/*
        adapted from
  parser.yy -- Bison/C++ parser for lilypond

  source file of the GNU LilyPond music typesetter

  (c)  1997--2001 Han-Wen Nienhuys <address@hidden>
           Jan Nieuwenhuizen <address@hidden>
        adapted for lilyfront (c) Richard Shann 2003

The result of parsing is a GList starting at lily_file
The data elements of the GList are node* where node is a structure with type, user_string and a union. Each node in the list represents the result of some rule below, with the user_string containing the part of the input file that parsed to that node.
The nodes are a simple list except
1) assignment statements where the assignee is a separate list entered in a symbol table.
2) SIMULTANEOUS blocks which are nodes pointing to a list for their contents
3) \score blocks which are nodes pointing to a list for their contents
After parsing the function create_score() is run on each score block. This creates a staff structure for each \context Staff encountered. The staff->measures field is pointed to the start of the block enclosed by the \context Staff. This block is traversed and the durations calculated to break the list into measures which are linked into the staff->measures list. After editing graphically denemo stype the whole list is traversed writing out the user_string fields or (where they are NULL) re-creating them from the data in the DenemoObject concerned.
Note that the structure node is arranged to have the same first fields as
DenemoObject so that either type can appear in the parse tree.

*/
#define YYDEBUG 1
#define YYPRINT fprintf
//#define DEBUG 1
#define YYTOKEN_TABLE 1
#include <string.h> /*for memcpy */
#include <stdlib.h> /* for system() */
#include <denemo/denemo.h>
#include "view.h" /* this includes many others - many are not protected against double inclusion */
#include "chordops.h"
#include "objops.h"
#include "twoints.h"
#include "processstaffname.h"
#include "tupletops.h"
#include "graceops.h"

#include <ctype.h>

#include "lyparserfuncs.h"
void lyrestart( FILE *new_file );
extern int lylineno;
static int parser_error_linenum = 0;
static gchar *parser_error_message = NULL; /* NULL for no parse error */
nodemin endcontextnode={endcontext,NULL}; /*the only instantiation of this
node, used as a marker */


#define stradd(m,n) if(n.user_string){m.user_string = \
        g_strconcat(m.user_string,n.user_string, NULL);}

#define LATER_MESSAGE(line) \
        call_parser_error("later at %d\n", __LINE__, line);return EOF


static GtkWidget *parser_error_dialog;
static int error_level_;
/* lexer states FIXME */
void push_note_state(void);
void push_figuredbass_state(void);
void push_chord_state(void);
void push_lyric_state(void);
void pop_state(void);
gboolean note_state_b(void);


int lylex(void);
static void lyerror(char *);

static void call_parser_error(gchar *text, int lineno, int input_line_number) {
   g_print("The parser needs finishing a lyparser.y:%d for this idiom"
           " to be usable\nThe problem occured at line number %d of the"
           " lily input", lineno, input_line_number);
parser_error("Edit or comment out the offending idiom if possible\n", input_line_number);
}

static GList *lily_file = NULL; /* the entire data generated by the parse */
/* before EOF there may be white space which won't
 *  be collected by any rule - the lexer passes it
* using set_trailing_white_space() */ static gchar *trailing_white_space = NULL;
GHashTable* name_value_pairs=NULL;
GHashTable* scm_identifiers=NULL;

#if 0
#define MALLOC_NODE(n, a) nodegeneric*n = \
        (nodegeneric*)g_malloc0(sizeof(nodegeneric));\
         memcpy(n, &a, sizeof(a));
#else
#define MALLOC_NODE(n, a) nodegeneric*n = \
        (nodegeneric*)g_malloc0(sizeof(nodegeneric));\
        n->type=a.type;n->user_string = a.user_string;
#endif


static void set_identifier (char* name, nodeglist *value) {     
  if(!name_value_pairs) 
    name_value_pairs = g_hash_table_new (g_str_hash, g_str_equal);
  g_hash_table_insert (name_value_pairs, (gpointer)name, (gpointer)value);
#if DEBUG
//g_warning("Set identifier %s to value %p\n",name, value);
g_print("Set identifier %s to value %s\n",name,  u_str(value->branch));
#endif  
}

static nodeglist * typed_glist (GList *g, guint t) {
        nodeglist *nodeg = (nodeglist *)g_malloc0(sizeof(nodeglist));
        nodeg->type = t;
        nodeg->branch = g;
        return nodeg;
}

#ifdef YYPRINT
static gchar * type_name(gint type);
#endif

gboolean
regular_identifier_b (char *s)
{
  gboolean v = TRUE;
  while (*s && v)
   {
        v = v && isalpha (*s);
        s++;
   }
  return v;
}



static int intlog2(int t) {
int i=1, n=0;
while ( (i<<n)<t) n++;
return n;
}

gboolean
is_duration_b (int t)
{
  return t && t == 1 << intlog2 (t);
}

node4i default_duration_;

static gchar * keytoname(gint pitch, gint enshift) {
gchar *ret;
        ret = g_strdup("a");
        *ret += pitch;
        for(;enshift>0;enshift--) ret = g_strconcat(ret,"is",NULL);
        for(;enshift<0;enshift++) ret = g_strconcat(ret,"es",NULL);/*tricksy - 
only one happens */
        return ret;
}

// needed for bison.simple's malloc () and free ()

#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>

#define YYERROR_VERBOSE 1




static void lyerror(char *s) {
        parser_error(s, lylineno);
}




%}

/* We use SCMs to do strings, because it saves us the trouble of
deleting them.  Let's hope that a stack overflow doesnt trigger a move
of the parse stack onto the heap. */


%{




%}


%union {
        nodemin minimal;
        nodegeneric generic;
        nodec c;
        nodei i;
        node2i t;
        node4i f;
        nodeb b;
        noden n;
        nodeid id;

        nodegstr gstr;
        nodemus music;  
        noder r;


        nodeglist *branch;
        GList *scm;
}

/* denemo special tokens */
%token DENEMO_MEASURES
/* make a distinction for initial and other clef changes etc for denemo which would otherwise issue a change of clef etc at the start of each staff */
%token <generic> TEXT
%token <gstr> staffcontext voicecontext lyricscontext figuredbasscontext
%token endcontext


%token<gstr> MUSICMODE
%token<gstr> TONEOPTION


%token<gstr> DYNAMICMARK






/* tokens which are not keywords */
%token <generic> AUTOCHANGE
%token <generic> ALIAS
%token <generic> APPLY
%token <generic> ARPEGGIO
%token <generic> DYNAMICSCRIPT
%token <generic> ACCEPTS
%token <generic> ALTERNATIVE
%token <generic> BAR
%token <generic> BREAK
%token <generic> BREATHE
%token <generic> CHORDMODIFIERS
%token <generic> CHORDS
%token <generic> CHAR_T
%token <generic> CLEF_
%token <generic> CM_T
%token <generic> CONSISTS
%token <generic> SEQUENTIAL
%token <generic> SIMULTANEOUS
%token <generic> GROBDESCRIPTIONS
%token <generic> CONSISTSEND
%token <generic> DENIES
%token <generic> DURATION
%token <generic> EXTENDER
%token <generic> FIGURES FIGURE_OPEN FIGURE_CLOSE
%token <generic> FIGURE_BRACKET_CLOSE FIGURE_BRACKET_OPEN
%token <generic> GLISSANDO
%token <generic> GRACE %token <generic> HEADER
%token <generic> HYPHEN
%token <generic> IN_T
%token <generic> INVALID
%token <generic> KEY
%token <generic> LYRICS
%token <generic> MARK
%token <generic> MARKUP
%token <generic> MULTI_MEASURE_REST
%token <generic> MIDI
%token <generic> MM_T
%token <generic> PITCH
%token <generic> DEFAULT
%token <generic> NAME
%token <generic> PITCHNAMES
%token <generic> NOTES
%token <generic> PAPER
%token <generic> PARTIAL_
%token <generic> PENALTY
%token <generic> PROPERTY
%token <generic> OVERRIDE SET REVERT %token <generic> PT_T
%token <generic> RELATIVE
%token <generic> REMOVE
%token <generic> REPEAT
%token <generic> ADDLYRICS
%token <generic> PARTCOMBINE
%token <generic> SCORE
%token <generic> SCRIPT
%token <generic> SKIP
%token <generic> SPANREQUEST
%token <generic> STYLESHEET
%token <generic> COMMANDSPANREQUEST
%token <generic> TEMPO
%token <generic> OUTPUTPROPERTY
%token <generic> TIME_T
%token <generic> TIMES
%token <generic> TRANSLATOR
%token <generic> TRANSPOSE
%token <generic> TYPE
%token <generic> UNSET
%token <minimal> CONTEXT
%token <generic> LAYOUT
%token <generic> LYRICSTO
%token <generic> LYRICMODE
%token <generic> NEWCONTEXT
%token <generic> LILYVERSION
%token <generic> DRUM_PITCH
%token <generic> MUSIC_FUNCTION
%token <i> REST

%token DOUBLE_ANGLE_CLOSE ">>"
%token DOUBLE_ANGLE_OPEN "<<"


/* escaped */
%token <generic> E_CHAR E_EXCLAMATION E_SMALLER E_BIGGER E_OPEN E_CLOSE
%token E_LEFTSQUARE E_RIGHTSQUARE E_TILDE
%token E_BACKSLASH
%token CHORD_BASS CHORD_COLON CHORD_MINUS CHORD_CARET
%token <generic> FIGURE_SPACE


%type <i> exclamations questions dots optional_rest %type <i> bass_number bass_mod
%type <generic>   br_bass_figure bass_figure figure_list figure_spec

%token <generic> '='
%token <generic> '{'
%token <generic> '<'
%token <generic> '}'
%token <generic> '>'
%token <generic> '|'
%token <generic> '/'
%token <generic> '*'
%token <generic> '('
%token <generic> ')'
%token <generic> '~'
%token <generic> '['
%token <generic> ']'
//%token <generic> ">>"
//%token <generic> "<<"

%token <i> '.'
%token <i> '?'
%token <i> '!'

%token <i> '\''
%token <i> ','


%token <i>        DIGIT
%token <t>        NOTENAME_PITCH
%token <t>        TONICNAME_PITCH
%token <t>        CHORDMODIFIER_PITCH
%token <id>       DURATION_IDENTIFIER
%token <t>      FRACTION
%token <id>       IDENTIFIER


%token <id>       SCORE_IDENTIFIER
%token <id>       MUSIC_OUTPUT_DEF_IDENTIFIER

%token <id>       NUMBER_IDENTIFIER
%token <id>       REQUEST_IDENTIFIER
%token <id>       MUSIC_IDENTIFIER TRANSLATOR_IDENTIFIER
%token <id> STRING_IDENTIFIER %token <gstr> SCM_IDENTIFIER %token <generic> RESTNAME
%token <generic>  SKIPNAME
%token <gstr> STRING_ %token <generic> SCM_T
%token <i>        UNSIGNED
%token <r>   REAL

%type <scm> output_def
%type <scm> lilypond_header lilypond_header_body toplevel_expression assignment score_block %type <scm> direction_less_char direction_less_event direction_reqd_event
%type <i> sub_quotes sup_quotes
%type <scm> simple_element event_chord command_element Simple_music Composite_music %type <scm> chord_body
%type <scm>       chord_body_element
%type <scm>       music_function_chord_body
%type <scm>       chord_body_elements
%type <scm>       Repeated_music
%type <scm>     Alternative_music
%type <i> tremolo_type
%type <i> bare_int  bare_unsigned
%type <i> script_dir
%type <i> octave_check

%type <branch> identifier_init %type <f> steno_duration %type <f> optional_notemode_duration multiplied_duration
%type <f>       verbose_duration
        
%type <scm>  post_events
%type <scm>  note_chord_element
%type <scm> gen_text_def
%type <scm> full_markup
%type <n>   steno_pitch pitch absolute_pitch
%type <n>   explicit_pitch steno_tonic_pitch

%type <scm>       chord_additions chord_subtractions chord_notes chord_step
%type <music>     chord
%type <scm>       chord_note chord_inversion chord_bass
%type <f> duration_length %type <t> fraction

%type <scm>  embedded_scm scalar
%type <scm> Music Sequential_music Simultaneous_music %type <scm> relative_music re_rhythmed_music part_combined_music
%type <scm>       property_def translator_change
%type <scm> Music_list
%type <scm>  music_output_def_body
%type <scm> shorthand_command_req
%type <scm>       post_event
%type <scm> command_req verbose_command_req
%type <scm>       extender_req
%type <scm> hyphen_req
%type <gstr> string %type <scm> bare_number number_expression number_term number_factor
%type <scm>       score_body

%type <scm>       translator_spec_block translator_spec_body
%type <scm>       tempo_event tempo_optional
%type <scm> notenames_body notenames_block chordmodifiers_block
%type <scm>       script_abbreviation



%left '-' '+'

/* We don't assign precedence to / and *, because we might need varied
prec levels in different prods */

%left UNARY_MINUS

%%

lilypond:       /* empty */
| lilypond toplevel_expression { if(lily_file) {
                        GList *ret;
                        ret = g_list_concat (lily_file, $2);
                } else {
                lily_file = $2;
                }
        }
| lilypond assignment { if(lily_file) {
                        GList *ret;
                        ret = g_list_concat(lily_file, $2);
                } else {
                lily_file = $2;
                }
        }
        | lilypond error {
                error_level_  = 1;
        }
        | lilypond INVALID      {
                error_level_  = 1;
        }
        ;

toplevel_expression:
        notenames_block                 
        { /* this is the \pitchnames thing in the include files - we will
             perhaps leave this out for now - see below for where the
             include file is parsed for the pitchname table - we have to
             recognize at least one set of pitchnames of course ...*/
$$ = g_list_append(NULL, $1); /* creates a new mudelaobj structure comprising the token TEXT,
             and the Gstring pointed to by input_text, which is reset to NULL.
We ignore the value of notename_block since we don't interpret it further */ }
        | chordmodifiers_block                  {
                $$ = $1;
        }
        | lilypond_header {
                $$ = $1;
        }
        | score_block {
                $$ =  $1;/* add this score to the root data list lily_file */
        }
        | output_def {
                $$ = $1;
        }
        | embedded_scm {
                $$ = $1;
        }
        ;


embedded_scm: SCM_T {
        MALLOC_NODE(n, $1);
        $$ = g_list_append(NULL,n);
        }
        | SCM_IDENTIFIER {
        MALLOC_NODE(n, $1);
        ((nodegstr*)n)->gstr = $1.gstr;
        $$ = g_list_append(NULL, n);
        }
        ;


chordmodifiers_block: CHORDMODIFIERS notenames_body { $$ = $2; /* intercept this at lexical level*/
        }
        ;

notenames_block:
        PITCHNAMES notenames_body   {  $$ = $2; }
        ;

notenames_body: embedded_scm {
        $$ = $1;
        }
        ;

lilypond_header_body: {
        $$ = NULL;
        }
| lilypond_header_body assignment { if($1) {
                        $$ = g_list_concat($1, $2);
                        }
else $$ = $2;
        }
        ;

lilypond_header: HEADER '{' lilypond_header_body '}' {
        MALLOC_NODE(n1, $1);
        MALLOC_NODE(n4, $4);
        n1->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
        $$ = g_list_append(NULL,n1);
        $$ = g_list_concat($$,$3);
        $$ = g_list_append($$, n4);     
        }
        |
        LILYVERSION STRING_
        {
                MALLOC_NODE(n,$1);
            g_free(n->user_string);
            n->user_string = $2.gstr->str;
        set_identifier("lilyversion", typed_glist (g_list_append(NULL,n), 
STRING_IDENTIFIER));
        $$ = NULL;
        }
        ;


/*
        DECLARATIONS
*/
assignment:
        STRING_ '=' identifier_init  {
                GList *ret;
                MALLOC_NODE (n1, $1);
                MALLOC_NODE (n2, $2);
                if (! regular_identifier_b ($1.gstr->str))
                {
                   g_warning (_("Identifier should have alphabetic characters"
                                " only please"));
                }
#if DEBUG
g_print("got to assignment with %s %s ",$1.gstr->str, $2.user_string);
                if ($3->type == STRING_IDENTIFIER)
                        g_print("%s\n", u_str($3->branch));
                else
                        g_print("type %d\n", $3->type);
#endif
                /* $3 because mid rule action deleted */
                set_identifier ($1.gstr->str, $3);
                
                ((nodeglist*)n2)->branch = $3->branch;
                ret = g_list_append(NULL, n2);
                $$ = g_list_prepend(ret, n1);
/*
 TODO: devise standard for protection in parser.

  The parser stack lives on the C-stack, which means that
all objects can be unprotected as soon as they're here.

*/
        }
        ;


identifier_init:
        score_block { /* I don't think this can ever get used, once defined! */
                $$ = typed_glist ($1, SCORE_IDENTIFIER);
        }
        | output_def {
                $$ = typed_glist ($1, MUSIC_OUTPUT_DEF_IDENTIFIER);
        }
        | translator_spec_block {
                $$ = typed_glist (g_list_append(NULL,$1), 
TRANSLATOR_IDENTIFIER);
        }
        | Music  {
                $$ = typed_glist ($1, MUSIC_IDENTIFIER);
        }
        | post_event {
                $$ = typed_glist ($1, REQUEST_IDENTIFIER);
        }
        | verbose_duration {
                MALLOC_NODE(n,$1);
                $$ = typed_glist (g_list_append(NULL,n), DURATION_IDENTIFIER);
        }
        | number_expression {
                $$ = typed_glist ($1, STRING_IDENTIFIER);
        }
        | string {
                nodegeneric x;
                x.type = STRING_IDENTIFIER;
                x.user_string = strdup($1.gstr->str);
                MALLOC_NODE(n,x);
                $$ = typed_glist (g_list_append(NULL,n), STRING_IDENTIFIER);
        }
        | embedded_scm  {
                $$ = typed_glist ($1, SCM_IDENTIFIER);
        }

        ;

translator_spec_block:
        TRANSLATOR '{' translator_spec_body '}'
                {
                $$ = $3;
        }
        ;

translator_spec_body:
        TRANSLATOR_IDENTIFIER   {
        }
        ;

/*
        SCORE
*/
score_block:
        SCORE '{' score_body '}'        {
                MALLOC_NODE(n,$1);
                n->user_string = g_strconcat($1.user_string, "{" , NULL);
                /*FIXME memory leak of $1,2 */
                ((nodeglist*)n)->post_user_string = "}";
                ((nodeglist*)n)->branch = $3;
                $$ = g_list_append(NULL, n);
        }
        ;

score_body:
        Music   {

                $$ = $1;
        }
        | SCORE_IDENTIFIER {
                $$ = g_list_append(NULL, $1.id);
        }
        | score_body lilypond_header    {
            // lyerror ("parser should have caught this score header");
                /*intercept this at lexical level*/     
        }
        | score_body output_def {
        //lyerror ("parser should have caught this");
                /*intercept this at lexical level*/
        }
        | score_body error {
        lyerror("score_body error");
        }
        ;


/*
        MIDI
*/
output_def:
        music_output_def_body '}' {
                MALLOC_NODE(n2, $2)
                $$ = g_list_append($1, n2);

/*              THIS-> lexer_-> scopes_.pop ();*/
        }
        ;


music_output_def_body:
        MIDI '{' tempo_optional {
                set_identifier("midi_tempo", typed_glist ($3, 
STRING_IDENTIFIER));
                $$ = $3;
        }
        | PAPER '{'     {
                /* caught by lexer - does not occur*/
                lyerror ("parser should have caught this-paper");
        }
        | LAYOUT '{' {
                /* caught by lexer - does not occur*/
                lyerror ("parser should have caught this - layout");
        }
        | music_output_def_body error {
                lyerror("music_output_def_body error");
                }
        ;

tempo_optional:
        /* empty */ {
                $$ = NULL;
        }
        | tempo_event {
                $$ = $1;
        }
        ;

tempo_event:
        TEMPO steno_duration '=' bare_unsigned  {
        MALLOC_NODE(n, $4);
        ((nodei*)n)->i = $4.i;
        $$ = g_list_append(NULL, n);
        }
        ;

/*
The representation of a  list is the

  (LIST . LAST-CONS)

 to have  efficient append.
*/
Music_list: /* empty */ {
                $$ = NULL;
        }
        | Music_list Music {
if($1) {
#if DEBUG
g_print("building up a music list now from %s to %s\n", u_str($1),  u_str($2));
#endif
                        $$ = g_list_concat($1, $2);
                }
else $$ = $2;
        }
/*
        | Music_list embedded_scm {

        }
*/
        | Music_list error {
        }
        ;


Music:
        Simple_music
        | Composite_music
                {
                        $$ = $1;
                }
        ;

Alternative_music:
        /* empty */ {
                        $$ = NULL;
        }
        | ALTERNATIVE '{' Music_list '}' {
                GList* ret;
                MALLOC_NODE(n1, $1);
                MALLOC_NODE(n4, $4);
                stradd($1, $2);
                ret = g_list_append(NULL, n1);
                ret = g_list_concat(ret, $3);
                $$ = g_list_append(ret, n4);
        }
        ;

Repeated_music:
        REPEAT string bare_unsigned Music Alternative_music
        {
        GList* ret;
        MALLOC_NODE(n1, $1);
        stradd($1, $2);
        stradd($1, $3);
        ret = g_list_append(NULL, n1);
        ret = g_list_concat(ret, $4);
        if($5)
                ret = g_list_concat(ret, $5);
        $$ = ret;

#if 0
        
                $$ = new_data_el (REPEAT,); FINISH THIS
                $$ = g_list_append ($$, new_data_el (STRINGL)....need to have 
the input strings
        for each token available - use a struct in yylval.
#endif
        }
        ;

Sequential_music:
        SEQUENTIAL '{' Music_list '}'           {
                MALLOC_NODE(n1, $1);
                n1->user_string = g_strconcat($1.user_string, "{", NULL);
                /*FIXME memory leak $1,2 and $4*/
                ((nodeglist*)n1)->post_user_string = "}" ;
                ((nodeglist*)n1)->branch = $3;
                $$ = g_list_prepend(NULL, n1);
        }
        | '{' Music_list '}'            {
                MALLOC_NODE(n1, $1);
                n1->user_string = "{";     
                ((nodeglist*)n1)->post_user_string = "}";
                /* FIXME memory leak of $3 */
                ((nodeglist*)n1)->type = SEQUENTIAL;
                ((nodeglist*)n1)->branch = $2;
                $$ = g_list_prepend(NULL, n1);
        }
        ;

Simultaneous_music:
        SIMULTANEOUS '{' Music_list '}'{
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                $$ = new Simultaneous_music (SCM_EOL);
                $$->set_mus_property ("elements", ly_car ($3));
                $$->set_spot(THIS->here_input());
#endif
        }
        |  simul_open  Music_list simul_close   {
/* we don't try to disambiguate chords on one stave from notes one to a staff here
           that is done in generate_chords() called by create_score() */
// RRR          MALLOC_NODE(n1, $2);
                nodegeneric*n1 = (nodegeneric*)g_malloc0(sizeof(nodegeneric));
                n1->user_string = "<<";
                ((nodeglist*)n1)->post_user_string = ">>";
                /* FIXME memory leak of $3 */
                ((nodeglist*)n1)->type = SIMULTANEOUS;
                ((nodeglist*)n1)->branch = $2;
                $$ = g_list_prepend(NULL, n1);
        }
        ;

Simple_music:
        event_chord             { $$ = $1; }
        | OUTPUTPROPERTY embedded_scm embedded_scm '=' embedded_scm     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                SCM pred = $2;
                if (!gh_symbol_p ($3))
                {
THIS->parser_error (_ ("Second argument must be a symbol")); }
                /* Should check # args */
                if (!gh_procedure_p (pred))
                {
                   THIS->parser_error (_ ("First argument must be a procedure"
                                          " taking one argument"));
                }

                Music *m = new Music (SCM_EOL);
                m->set_mus_property ("predicate", pred);
                m->set_mus_property ("grob-property", $3);
                m->set_mus_property ("grob-value",  $5);
                m->set_mus_property ("iterator-ctor",
                Output_property_music_iterator::constructor_cxx_function);

                $$ = m;
#endif
        }
        | MUSIC_IDENTIFIER { /* this may be ok now ... */
/* has to be big enough for DenemoObject access eg when writing start_ticks in break into measures... */
                nodeid *n = (nodeid*)g_malloc0(sizeof(nodegeneric));
                n->type = $1.type;
                n->user_string = $1.user_string;
                n->id = $1.id;
                $$ = g_list_append(NULL, n);

        }
        | property_def {

$$ = $1; /* FIXME - we really don't want to put all these nodes into
           the music, as denemo will have to go over them -
           amalgamate the strings into a TEXT node */
                

        }
        | translator_change {
        LATER_MESSAGE(@$.first_line);
        }
        ;


Composite_music:
        CONTEXT STRING_ Music   {
                MALLOC_NODE (n1, $1);
if(!strcmp("Staff",$2.gstr->str)) n1->type = staffcontext; else if(!strcmp("Voice",$2.gstr->str)) n1->type = voicecontext; else if(!strcmp("Lyrics",$2.gstr->str)) n1->type = lyricscontext; else if(!strcmp("FiguredBass",$2.gstr->str)) n1->type = figuredbasscontext; else n1->type = TEXT;/*ignore other contexts at present */ n1->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
                if (n1->type == TEXT)
                        $$ = g_list_prepend($3, n1);
                else
$$ = g_list_append(g_list_prepend($3, n1), &endcontextnode); /* FIXME memory leak of $2 */
        }
        | AUTOCHANGE STRING_ Music      {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Music * chm = new Music_wrapper (SCM_EOL);
                chm->set_mus_property ("element", $3);
chm->set_mus_property ("iterator-ctor", Auto_change_iterator::constructor_cxx_function);

                scm_gc_unprotect_object ($3);
chm->set_mus_property ("what", $2);
                $$ = chm;
                chm->set_spot (*$3->origin ());
#endif
        }
        | GRACE Music {
        DenemoObject *start, *end;
        start = newgracestart();
        end =  newgraceend();
        start->user_string = $1.user_string;
        /* prevent denemo generating a "}" string
           this will take some effort to handle better than this hack. FIXME */
        end->user_string = g_strdup(" ");
        $$ = g_list_append(g_list_prepend ($2,start ), end);

        }
        | CONTEXT string '=' string Music {
                MALLOC_NODE(n1, $1);
                ((nodegstr*)n1)->gstr = $4.gstr;
if(!strcmp("Staff",$2.gstr->str)) n1->type = staffcontext; else if(!strcmp("Voice",$2.gstr->str)) n1->type = voicecontext; else if(!strcmp("Lyrics",$2.gstr->str)) n1->type = lyricscontext; else if(!strcmp("FiguredBass",$2.gstr->str)) n1->type = figuredbasscontext; else n1->type = TEXT;/*ignore other contexts at present */ n1->user_string = g_strconcat($1.user_string, $2.user_string, "=", $4.user_string, NULL);
                if (n1->type == TEXT)
                        $$ = g_list_prepend($5, n1);
                else
$$ = g_list_append(g_list_prepend($5, n1), &endcontextnode); /* FIXME memory leak $2 $3 $4 */
        }
        | NEWCONTEXT STRING_ Music      {
                MALLOC_NODE (n1, $1);
if(!strcmp("Staff",$2.gstr->str)) n1->type = staffcontext; else if(!strcmp("Voice",$2.gstr->str)) n1->type = voicecontext; else if(!strcmp("Lyrics",$2.gstr->str)) {
                  n1->type = lyricscontext;
                }
else if(!strcmp("FiguredBass",$2.gstr->str)) n1->type = figuredbasscontext; else n1->type = TEXT;/*ignore other contexts at present */ n1->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
                if (n1->type == TEXT)
                        $$ = g_list_prepend($3, n1);
                else
$$ = g_list_append(g_list_prepend($3, n1), &endcontextnode); }
        | TIMES fraction Music  

        {DenemoObject *tupopen, *tupclose;
                tupopen = newtupopen ($2.t.a, $2.t.b);
                tupclose = newtupclose ();
                g_assert(ntype($3)==SEQUENTIAL);
                
tupopen->user_string = g_strconcat($1.user_string, $2.user_string, u_str($3),
                                                   NULL);
                tupclose->user_string = u_post_str($3);
                $$ = g_list_append(g_list_prepend (br($3), tupopen), tupclose);
        }
| Repeated_music { $$ = $1; } | Simultaneous_music { $$ = $1; } | Sequential_music { $$ = $1; }
        | TRANSPOSE pitch pitch Music {

                /* could we try to display transposed?? later FIXME */
                GList* ret;
                MALLOC_NODE(n1, $1);
                n1->user_string = g_strconcat($1.user_string, $2.user_string,  
$3.user_string,
                                              NULL);    
                ret = g_list_append(NULL, n1);
                $$ = g_list_concat(ret, $4);
                
                
        }
        | TRANSPOSE steno_tonic_pitch Music {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                $$ = new Transposed_music (SCM_EOL);
                Music *p = $3;
                Pitch pit = *unsmob_pitch ($2);

                p->transpose (pit);
                $$->set_mus_property ("element", p);
                scm_gc_unprotect_object (p);
#endif  
        }
        | APPLY embedded_scm Music  {
                u_str($2) = g_strconcat($1.user_string, u_str($2), NULL);
                $$ = g_list_concat($2, $3);
        }
        | NOTES
                { push_note_state (); }
                /* cont */
        Music
{ MALLOC_NODE(n1, $1);
                $$ = g_list_prepend($3, n1);
                pop_state();
                }
        | FIGURES
                { push_figuredbass_state (); }
        Music
                {
                MALLOC_NODE(n1, $1);
                $$ = g_list_prepend($3, n1);
                pop_state();
        }
        | CHORDS
                { push_chord_state (); }
        Music
                {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                  Music * chm = new Un_relativable_music ;
                  chm->set_mus_property ("element", $3);
                  scm_gc_unprotect_object ($3->self_scm());
                  $$ = chm;

                  THIS->lexer_->pop_state ();
#endif  
        }
        | LYRICS
                { push_lyric_state (); }
        Music
                {
LATER_MESSAGE(@$.first_line);
                MALLOC_NODE(n, $1)
                $$ = g_list_prepend($3, n);
                pop_state();
        }
| LYRICMODE { push_lyric_state (); }
        Music
                {
                MALLOC_NODE(n, $1)
                $$ = g_list_prepend($3, n);
                pop_state();
        }
| relative_music { $$ = $1; } | re_rhythmed_music { $$ = $1; } | part_combined_music { $$ = $1; } ;

relative_music:
        RELATIVE absolute_pitch Music {
        MALLOC_NODE(n, $1)
        g_warning("\\relative not yet handled - do not edit graphically");
        n->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
        $$ = g_list_prepend($3, n);
        /* we have to record the pitch in the node and then use it to start
        relative interpretation of future CHORD nodes FIXME
well, not really we have to record it in a variable akin to default_duration_ which can then be tracked to determine the semantics of future notes??? Or has
        Music already been interpreted when this rule is activated...  */
        }
        ;

re_rhythmed_music:
        ADDLYRICS Music Music {
                MALLOC_NODE(n, $1);
                ((nodeglist*)n)->branch = g_list_append(g_list_append(NULL, 
g_list_append(NULL, $2)), g_list_append(NULL, $3)); /* ADDLYRICS is a branch 
containing two GLists */
                $$ = g_list_append(NULL, n);
        }
        | LYRICSTO STRING_  Music {
                GList *g;
                MALLOC_NODE(n, $1);
                g_free(n->user_string);
                if (*$2.gstr->str == '\"')
                {
                    GString * name = g_string_erase ($2.gstr, 0, 1);
                    name = g_string_truncate(name, name->len - 1);
                    n->user_string = name->str;
                }
                else
                    n->user_string = $2.gstr->str;
                if (ntype ($3) == NEWCONTEXT)
                {
                    g = $3;
                    $3 = g_list_remove_link ( $3, g);
                    g_list_free(g);
                }
                ((nodeglist*)n)->branch = $3;
                $$ = g_list_append(NULL, n);
        }
        ;

part_combined_music:
        PARTCOMBINE STRING_ Music Music {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Part_combine_music * p = new Part_combine_music (SCM_EOL);

                p->set_mus_property ("what", $2);
p->set_mus_property ("elements", gh_list ($3,$4, SCM_UNDEFINED));
                scm_gc_unprotect_object ($3);
scm_gc_unprotect_object ($4);
                $$ = p;
#endif  
        }
        ;

translator_change:
        TRANSLATOR STRING_ '=' STRING_  {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Music * t = new Music (SCM_EOL);
                t->set_mus_property ("iterator-ctor",
                        Change_iterator::constructor_cxx_function);
                t-> set_mus_property ("change-to-type", $2);
                t-> set_mus_property ("change-to-id", $4);

                $$ = t;
                $$->set_spot (THIS->here_input ());
#endif  
        }
        ;

property_def:
        PROPERTY STRING_ '.' STRING_ '='  scalar {
        MALLOC_NODE(n, $1)
n->user_string = g_strconcat($1.user_string, $2.user_string, ".", $4.user_string, "=", u_str($6), NULL);
        $$ = g_list_append(NULL, n); /* FIXME memory leak*/
        }
        | PROPERTY STRING_ '.' STRING_ UNSET {
        MALLOC_NODE(n, $1)
n->user_string = g_strconcat($1.user_string, $2.user_string, $3.user_string, $4.user_string, $5.user_string, NULL);
        $$ = g_list_append(NULL, n); /* FIXME memory leak*/
        }
        | SET STRING_ '.' STRING_ '=' embedded_scm {
        MALLOC_NODE(n, $1)
n->user_string = g_strconcat($1.user_string, $2.user_string, $3.user_string, $4.user_string, $5.user_string, u_str($6), NULL);
        $$ = g_list_append(NULL, n);
        }
        | SET STRING_ '.' STRING_ '=' STRING_ {
        MALLOC_NODE(n, $1)
n->user_string = g_strconcat($1.user_string, $2.user_string, $3.user_string, $4.user_string, $5.user_string, $6.user_string, NULL);
        $$ = g_list_append(NULL, n);
        }
        | OVERRIDE STRING_ '.' STRING_ '=' embedded_scm {
        MALLOC_NODE(n, $1);
n->user_string = g_strconcat($1.user_string, $2.user_string, $3.user_string, $4.user_string, $5.user_string, u_str($6), NULL);
        $$ = g_list_append(NULL, n);
        }
        | OVERRIDE STRING_ embedded_scm '=' embedded_scm {
        MALLOC_NODE(n, $1);
n->user_string = g_strconcat($1.user_string, $2.user_string, u_str ($3), $4.user_string, u_str ($5), NULL);
        $$ = g_list_append (NULL, n);
        }
        | REVERT STRING_ embedded_scm {
        MALLOC_NODE(n, $1);
n->user_string = g_strconcat($1.user_string, $2.user_string, u_str ($3), NULL);
        $$ = g_list_append (NULL, n);
        }

        | PROPERTY STRING_ '.' STRING_ REVERT embedded_scm {
        MALLOC_NODE(n, $1);
n->user_string = g_strconcat($1.user_string, $2.user_string, $3.user_string, $4.user_string, $5.user_string, u_str ($6), NULL);
        $$ = g_list_append (NULL, n);
        }
        ;


scalar:
string { MALLOC_NODE(n, $1);$$ = g_list_append (NULL, n); /*FIXME copy value */}
        | bare_int      { MALLOC_NODE(n, $1);$$ = g_list_append (NULL, n);
                          /*FIXME copy value */ }
        | embedded_scm  { $$ = $1; }
        ;

pre_events: /* empty */
        ;

event_chord:
        pre_events simple_element post_events   {
/* things like start cresc, simple_element end cresc */

        set_post_events ((DenemoObject *) ($2->data), u_str ($2), $3);
                        
        $$ = $2;/* FIXME memory leak */
        }
        | command_element {
        $$ = $1;
        }
        | note_chord_element {
                $$ = $1;
        }
        ;

note_chord_element:
        chord_body optional_notemode_duration post_events
        {
            GList *firstchord = br ($1);
            if (firstchord && firstchord->data)
                changedur ((DenemoObject *)(firstchord->data), $2.t1.a, 
$2.t1.b);
            set_post_events ((DenemoObject *) ($1->data), u_str ($1), $3);
            $$ = $1;
        }
        ;


chord_open: '<'
        ;

chord_close: '>'
        ;

chord_body:
        chord_open chord_body_elements chord_close
        {
            nodegeneric*n1 = (nodegeneric*)g_malloc0(sizeof(nodegeneric));
            n1->user_string = "<";      
            ((nodeglist*)n1)->post_user_string = ">";
            ((nodeglist*)n1)->type = SIMULTANEOUS;
            ((nodeglist*)n1)->branch = $2;
            $$ = g_list_prepend(NULL, n1);
        }
        ;

chord_body_elements:
        /* empty */          { $$ = NULL; }
        | chord_body_elements chord_body_element {
                $$ = g_list_concat ($1, $2);
        }
        ;

music_function_chord_body:
        MUSIC_FUNCTION {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_list_2 ($1, make_input (@$));
#endif
        }

        ;

chord_body_element:
        pitch exclamations questions  octave_check post_events {
                DenemoObject *mud = newchord ( 0, 0, 0);

                if (!note_state_b ())
                        lyerror (_ ("Have to be in Note mode for notes"));

                addtone ( mud, $1.n.mid_c_offset, $1.n.enshift, 0);/*FIXME 
should be
                                                        using $1.n directly */

#define no ((note*)((((chord *)mud->object)->notes)->data))
if ($3.i % 2) {
                        no->showaccidental = TRUE;
                        ( (chord *)mud->object)->hasanacc = TRUE;
                }
                if ($2.i % 2 ) {
                        no->showaccidental = TRUE;
                        ( (chord *)mud->object)->hasanacc = TRUE;
                }
#undef no

                mud->user_string = $1.user_string;
                if ($2.i) stradd ( (*mud),$2);
                if ($3.i) stradd ( (*mud),$3);
                //stradd ( (*mud),$4);

                set_post_events (mud, mud->user_string, $5);
                $$ = g_list_append (NULL,mud);
        }
        | DRUM_PITCH post_events {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Music *n = MY_MAKE_MUSIC ("NoteEvent");
                n->set_property ("duration", $2);
                n->set_property ("drum-type", $1);
                n->set_spot (@$);

                if (scm_is_pair ($2)) {
                        SCM arts = scm_reverse_x ($2, SCM_EOL);
                        n->set_property ("articulations", arts);
                }
                $$ = n;
#endif // LATER
        }
        | music_function_chord_body {
LATER_MESSAGE(@$.first_line);
        }
        ;

command_element:
        command_req {
        $$ = $1;
}       
        | E_LEFTSQUARE {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Span_req *l = new Span_req;
                l->set_span_dir (START);
                l->set_mus_property ("span-type", scm_makfrom0str ("ligature"));
                l->set_spot (THIS->here_input ());

                $$ = new Request_chord (SCM_EOL);
                $$->set_mus_property ("elements", gh_cons (l, SCM_EOL));
          scm_gc_unprotect_object (l->self_scm ());
                $$->set_spot (THIS->here_input ());
#endif
        }
        | E_RIGHTSQUARE {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Span_req *l = new Span_req;
                l->set_span_dir (STOP);
                l->set_mus_property ("span-type", scm_makfrom0str ("ligature"));
                l->set_spot (THIS->here_input ());

                $$ = new Request_chord (SCM_EOL);
                $$->set_mus_property ("elements", gh_cons (l, SCM_EOL));
                $$->set_spot (THIS->here_input ());
          scm_gc_unprotect_object (l->self_scm ());
#endif
        }
        | E_BACKSLASH {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                $$ = new Music (gh_list (gh_cons (ly_symbol2scm ("name"), ly_symbol2scm 
("separator")), SCM_UNDEFINED));
                $$->set_spot (THIS->here_input ());
#endif
        }
        | '|'      {
        MALLOC_NODE(n, $1);
        $$ = g_list_append(NULL, n); /* this node used to be used by denemo to 
split the glist into measures */
        }
        | BAR STRING_   {
                        MALLOC_NODE(n, $1);

                  n->user_string = g_strconcat($1.user_string, $2.user_string, 
NULL);/* FIXME memory leaks */
                  $$ = g_list_append(NULL, n);
        }
        | PARTIAL_ duration_length      {
        //      g_warning ("\\partial not currently supported");
                DenemoObject *mud = newchord( $2.t1.a, $2.t1.b,0);
                mud->type = PARTIAL;
/* FIXME - we need to store all four ints then use them to determine how much 
measure to skip */
                mud->user_string = g_strconcat($1.user_string, 
$2.user_string,NULL);
                /* FIXME memory leaks on strings concatenated */
                $$ = g_list_append(NULL,mud);
        }
        | CLEF_ STRING_  {
                DenemoObject *mud = dnm_newclefobj 
(cleftypefromname($2.gstr->str));
                mud->user_string = g_strconcat($1.user_string, 
$2.user_string,NULL);
                /* FIXME memory leaks on strings concatenated */
                $$ = g_list_append(NULL,mud);
        }
        | TIME_T fraction  {
                DenemoObject *mud = dnm_newtimesigobj ($2.t.a, $2.t.b);
                mud->user_string = g_strconcat($1.user_string, 
$2.user_string,NULL);
                /* FIXME memory leaks on strings concatenated */
                $$ = g_list_append(NULL,mud);
        }
        | BREAK { /* ignore */
                $$ = NULL;
        }
        ;

command_req:
        shorthand_command_req   { $$ = $1; }
        | verbose_command_req   { $$ = $1; }
        ;

shorthand_command_req:
        extender_req {
                $$ = $1;
        }
        | hyphen_req {
                $$ = $1;
        }
        | '['           {
        MALLOC_NODE(n, $1)
        $$ = g_list_append(NULL, n);/* FIXME denemo should know about this */
        }
        | ']'           {       
        MALLOC_NODE(n, $1)
        $$ = g_list_append(NULL, n);/* FIXME denemo should know about this */


        }
        | BREATHE {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                $$ = new Breathing_sign_req;
#endif
        }
        | E_TILDE {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                $$ = new Porrectus_req;
#endif
        }
        ;

verbose_command_req:
        COMMANDSPANREQUEST bare_int STRING_ { /*TODO: junkme */
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Span_req * sp = new Span_req;
                sp-> set_span_dir ( Direction ($2));
                sp->set_mus_property ("span-type",$3);
                sp->set_spot (THIS->here_input ());
                $$ = sp;
#endif
        }
        | MARK DEFAULT  {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Mark_req * m = new Mark_req;
                $$ = m;
#endif
        }
        | MARK scalar {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Mark_req *m = new Mark_req;
                m->set_mus_property ("label", $2);
                $$ = m;
#endif
        }
        | SKIP duration_length {
        /* denemo doesn't want to know? */
        MALLOC_NODE(n, $1)

        n->type=TEXT;
        n->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
        $$ = g_list_append(NULL,n);

#ifdef LATER
                Skip_req * skip = new Skip_req;
                skip->set_mus_property ("duration", $2);

                $$ = skip;
#endif
        }
        | tempo_event {
LATER_MESSAGE(@$.first_line);
//              $$ = $1;
        }
        | KEY DEFAULT {
LATER_MESSAGE(@$.first_line);
#ifdef LATER

                Key_change_req *key= new Key_change_req;
                $$ = key;
#endif
        }
        | KEY NOTENAME_PITCH MUSICMODE  {
                gchar *keyname = keytoname($2.t.a, $2.t.b);
                DenemoObject *mud;
                /* the convoluted conversion is due to historical mismatch of 
lily and denemo*/
                if (!strcmp($3.gstr->str, "minor"))        
                        mud = dnm_newkeyobj(keynametonumber(keyname)-3, TRUE, 
0);
                else
                        mud = dnm_newkeyobj(keynametonumber(keyname), FALSE, 0);
                mud->user_string = g_strconcat($1.user_string, $2.user_string, 
$3.user_string, NULL);
                $$ = g_list_append(NULL,mud);
        }
        ;

post_events:
        {
        $$ = NULL;
        }
        | post_events post_event {
                if($1) {
                        $$ = g_list_concat($1, $2);
                        }
else $$ = $2;
        }
        ;

post_event:
        direction_less_event {
                $$ = $1;
        }
        | script_dir direction_reqd_event {
           /* script_dir is an integer saying whether up down or centred
              denemo doesn't understand this yet */
            u_str($2) = g_strconcat ( $1.user_string, u_str($2), NULL);
            $$ = $2;
        }
        | script_dir direction_less_event {
           /* script_dir is an integer saying whether up down or centred
              denemo doesn't understand this yet */
            u_str($2) = g_strconcat ( $1.user_string, u_str($2), NULL);
            $$ = $2;
        }
        ;


direction_reqd_event:
        gen_text_def {
                $$ = $1;
        }
        | script_abbreviation {
                $$ = $1;
        }
        ;

octave_check:
        /**/ {  }
        | '='  { $$.i = 0; }
        | '=' sub_quotes { $$ = $2; }
        | '=' sup_quotes { $$ = $2; }
        ;


sup_quotes:
        '\'' {
                $1.i = 1;
                $$ = $1;
        }
        | sup_quotes '\'' {
                $1.i ++;
                $1.user_string = g_strconcat($1.user_string,$2.user_string, 
NULL);/*FIXME memory leak */
                $$ = $1;
        }
        ;

sub_quotes:
        ',' {
                $1.i = 1;
                $$ = $1;        
        }
        | sub_quotes ',' {
                $1.i ++ ;
                $1.user_string = g_strconcat($1.user_string,$2.user_string, 
NULL);/*FIXME memory leak */
                $$ = $1;
        }
        ;

steno_pitch:
        NOTENAME_PITCH  {
                char notename = 'a' + $1.t.a;
                int enshift = $1.t.b;
                $$.user_string = $1.user_string;
                $$.n.enshift = enshift;
                $$.n.mid_c_offset = pitchtomid_c_offset (notename, 0);          
        }
        | NOTENAME_PITCH sup_quotes     {
                char notename = 'a' + $1.t.a;
                int enshift = $1.t.b;
                int sups=$2.i;
                $$.user_string = g_strconcat($1.user_string, $2.user_string, 
NULL);
                $$.n.enshift = enshift;
                $$.n.mid_c_offset = pitchtomid_c_offset (notename, sups);
        }
        | NOTENAME_PITCH sub_quotes      {
                char notename = 'a' + $1.t.a;
                int enshift = $1.t.b;
                int subs = -$2.i;
                $$.user_string = g_strconcat($1.user_string, $2.user_string, 
NULL);
                $$.n.enshift = enshift;
                $$.n.mid_c_offset = pitchtomid_c_offset (notename, subs);
        }
        ;

/*
ugh. duplication
*/

steno_tonic_pitch:
        TONICNAME_PITCH {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
#endif
        }
        | TONICNAME_PITCH sup_quotes    {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Pitch p = *unsmob_pitch ($1);
                p.octave_ +=  $2;
                $$ = p.smobbed_copy ();
#endif
        }
        | TONICNAME_PITCH sub_quotes     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Pitch p =* unsmob_pitch ($1);

                p.octave_ +=  -$2;
                $$ = p.smobbed_copy ();

#endif
        }
        ;

pitch:
        steno_pitch {
                $$ = $1;
        }
        | explicit_pitch {
                $$ = $1;
        }
        ;

explicit_pitch:
        PITCH embedded_scm {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $2;
                if (!unsmob_pitch ($2)) {
                        THIS->parser_error (_f ("Expecting musical-pitch 
value", 3));
                         $$ = Pitch ().smobbed_copy ();
                }
#endif
        }
        ;

verbose_duration:
        DURATION embedded_scm   {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $2;
                if (!unsmob_duration ($2))
                {
                        THIS->parser_error (_ ("Must have duration object"));
                        $$ = Duration ().smobbed_copy ();
                }
#endif
        }
        ;

extender_req:
        EXTENDER {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                if (!THIS->lexer_->lyric_state_b ())
                        THIS->parser_error (_ ("Have to be in Lyric mode for 
lyrics"));
                $$ = new Extender_req;
#endif
        }
        ;

hyphen_req:
        HYPHEN {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                if (!THIS->lexer_->lyric_state_b ())
                        THIS->parser_error (_ ("Have to be in Lyric mode for 
lyrics"));
                $$ = new Hyphen_req;
#endif
        }
        ;

direction_less_event:
         direction_less_char {
              $$ = $1;
        }
        | DYNAMICMARK {
            MALLOC_NODE(n, $1);
            ((nodegstr*)n)->gstr = $1.gstr;
            $$ = g_list_append(NULL, n);
        }
        | TONEOPTION {
            MALLOC_NODE(n, $1);
            ((nodegstr*)n)->gstr = $1.gstr;
            $$ = g_list_append(NULL, n);
        }
        | tremolo_type  {
LATER_MESSAGE(@$.first_line);
        }
        ;
direction_less_char:
        '('     {
            MALLOC_NODE(n, $1)
            $$ = g_list_append(NULL, n);
        }
        | ')'   {
            MALLOC_NODE(n, $1)
            $$ = g_list_append(NULL, n);
        }
        | '~'   {       /* tie */
            MALLOC_NODE(n, $1)
            $$ = g_list_append(NULL, n);
        }
        | E_SMALLER {
            MALLOC_NODE(n, $1)
            $$ = g_list_append(NULL, n);
        }
        | E_BIGGER {
            MALLOC_NODE(n, $1)
            $$ = g_list_append(NULL, n);
        }
        | E_EXCLAMATION {
            MALLOC_NODE(n, $1)
            $$ = g_list_append(NULL, n);
        }
        | E_OPEN        {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Span_req* s= new Span_req;
                $$ = s;
                s->set_mus_property ("span-type", scm_makfrom0str ( 
"phrasing-slur"));
                s->set_spot (THIS->here_input());
#endif
        }
        ;

full_markup:
        MARKUP {
LATER_MESSAGE(@$.first_line);
        }
        ;
gen_text_def:
        full_markup {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Music *t = MY_MAKE_MUSIC ("TextScriptEvent");
                t->set_property ("text", $1);
                t->set_spot (@$);
                $$ = t;
#endif
        }
/* JAN
        DYNAMICMARK {
        MALLOC_NODE(n, $1);
        ((nodegstr*)n)->gstr = $1.gstr;
        $$ = g_list_append(NULL, n);
        }
*/
        | string {
            MALLOC_NODE(n, $1);
            ((nodegstr*)n)->gstr = $1.gstr;
            $$ = g_list_append(NULL, n);
        }
        | DIGIT {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                String ds = to_string ($1);
                Text_script_req* t = new Text_script_req;
                SCM finger = ly_symbol2scm ("finger");
                t->set_mus_property ("text",  scm_makfrom0str (ds.to_str0 ()));
                t->set_mus_property ("text-type" , finger);
                t->set_spot (THIS->here_input ());
                $$ = t;
#endif
        }
        ;

script_abbreviation:
        '^'             {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Hat");
#endif
        }
        | '+'           {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Plus");
#endif
        }
        | '-'           {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Dash");
#endif
        }
        | '|'           {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Bar");
#endif
        }
        | '>'                {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Larger");
#endif
        }
        | '.'           {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Dot");
#endif
        }
        | '_' {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_makfrom0str ("Underscore");
#endif
        }
        ;

script_dir:
        '_'     { /* $$ = DOWN; */ }
        | '^'   {  /* $$ = UP; */ }
        | '-'   {  /* $$ = CENTER; */ }
        ;


absolute_pitch:
        steno_pitch     {
                $$ = $1;
        }
        ;

duration_length:
        multiplied_duration {
        $$ = $1;
        }
        | verbose_duration {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
#endif
        }       
        ;

optional_notemode_duration:
        {
        $$ = default_duration_;
        }
        | multiplied_duration   {
                $$ = $1;
                 default_duration_.t1.a = $1.t1.a;
                 default_duration_.t1.b = $1.t1.b;
        }
        | verbose_duration {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
                THIS->default_duration_ = *unsmob_duration ($$);
#endif
        }       
        ;

steno_duration:
        bare_unsigned dots              {
                int l = 0;
                if (!is_duration_b ($1.i))
                        lyerror ("value not a duration");/*  $1.i */
                else
                        l =  intlog2 ($1.i);

                if($2.i) $$.user_string = g_strconcat($1.user_string, 
$2.user_string, NULL);
                $$.t1.a = l;
                $$.t1.b =$2.i;
        }
        | DURATION_IDENTIFIER dots      {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Duration *d =unsmob_duration ($1);
                Duration k (d->duration_log (),d->dot_count () + $2);
                $$ = k.smobbed_copy ();
#endif
        }
        ;




multiplied_duration:
        steno_duration { /* note 4 integers are used for these */
                $1.t2.a = 1;
                $1.t2.b = 1;
                $$ = $1;
        }
        | multiplied_duration '*' bare_unsigned {/* note 4 integers are used 
for these */
        stradd($1,$2);  
        stradd($1,$3);
$1.t2.a *= $3.i; $$ = $1;

#ifdef LATER
                $$ = unsmob_duration ($$)->compressed ( $3) .smobbed_copy ();
#endif
        }
        | multiplied_duration '*' FRACTION {/* note 4 integers are used for 
these */
        stradd($1,$2);  
        stradd($1,$3);
        $1.t2.a *= $3.t.a; /* numerator of fraction */
        $1.t2.b  = $3.t.b; /* denominator of fraction */
        $$ = $1;

#ifdef LATER
                Rational  m (gh_scm2int (ly_car ($3)), gh_scm2int (ly_cdr 
($3)));

                $$ = unsmob_duration ($$)->compressed (m).smobbed_copy ();
#endif
        }
        ;

fraction:
        FRACTION { $$ = $1; }
        | UNSIGNED '/' UNSIGNED {

                $$.user_string =  g_strconcat($1.user_string, $2.user_string, 
$3.user_string, NULL);
                $$.t.a = $1.i;
                $$.t.b = $3.i;
        }
        ;

dots:
        /* empty */     { $$.i = 0; }
| dots '.' { if($1.i == 0){
                        $$.i = 1;
                        $$.user_string = $2.user_string;
                } else {
                        $$.user_string = g_strconcat($1.user_string, 
$2.user_string, NULL);/* FIXME memory leak $2*/
                                
                        $$.i = $1.i+1;
                 }
        }
        ;

tremolo_type: ':' {
                $$.i = 0;
        }
        | ':' bare_unsigned {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                if (!is_duration_b ($2))
                        THIS->parser_error (_f ("not a duration: %d", $2));
                $$ = $2;
#endif
        }
        ;


bass_number:
        DIGIT
        | UNSIGNED { $$ = $1;}
        ;

bass_mod:
        '-'     { $$.i = -1; }
| '+' { $$.i = 1; } | '!' { $$.i = 0; }
        ;

bass_figure:
        FIGURE_SPACE {
                $$ = $1;
        }
        | bass_number  {
                $$.user_string = $1.user_string;
        }
        | bass_figure bass_mod {
                $$.user_string = g_strconcat($1.user_string, $2.user_string, 
NULL);
        }
        ;

br_bass_figure:
        '[' bass_figure {
                $$ = $1;
        }
        | bass_figure   {

                $$ = $1;

        }
        | br_bass_figure ']' {
                $$ = $1;
        }
        ;

figure_list:
        /**/            {

                $$.user_string = g_strdup("");

        }
        | figure_list br_bass_figure {
                $$.user_string = g_strconcat($1.user_string, $2.user_string, 
NULL);
        }
        ;

figure_spec:
        FIGURE_OPEN figure_list FIGURE_CLOSE {
                $$.user_string = g_strconcat($1.user_string, $2.user_string, 
$3.user_string, NULL);
        }
        ;


optional_rest:
        /* empty */   { $$.i = 0; }
        | REST { $$.i = 1; }
        ;

        ;

simple_element:
        pitch exclamations questions octave_check optional_notemode_duration 
optional_rest  {
/* pitch is a node* with union a denemo note, exclamations and questions are nodei, optional_notemode_duration is now a node4i, but I don't know how to calculate with the multiplier fraction in the top two ints, optional rest is boolean for the \rest keyword appearing after a note - it turns it into a rest the duration is on the chord structure, the pitch on the note structure.
                print 
*(note*)(((DenemoObject*)(((GList*)(((staff*)si->thescore->data)->measures->data))->data))->u.chordval.notes.data)
*/ DenemoObject *mud = newchord( $5.t1.a, $5.t1.b, 0);

                if (!note_state_b ())
                        lyerror (_ ("Have to be in Note mode for notes"));
                if ($6.i)
                        /* this is a rest vertically placed at the note
                           no special representation in denemo yet */;
                else {
                        addtone( mud, $1.n.mid_c_offset, $1.n.enshift, 
0);/*FIXME should be
                                                        using $1.n directly */

#define no ((note*)((((chord *)mud->object)->notes)->data))            
                        if ($3.i % 2) {
                                no->showaccidental = TRUE;
                                ((chord *)mud->object)->hasanacc = TRUE;
                        }
                        if ($2.i % 2 ) {
                                no->showaccidental = TRUE;
                                ((chord *)mud->object)->hasanacc = TRUE;
                        }
#undef no
                }
                mud->user_string = $1.user_string;
                if ($2.i) stradd ( (*mud),$2);
                if ($3.i) stradd ( (*mud),$3);
                stradd ( (*mud),$5);
                if ($6.i) stradd ( (*mud),$6); /* FIXME memory leaks on strings 
concatenated */

                $$ = g_list_append (NULL,mud);
        }       
        | RESTNAME optional_notemode_duration           {
        /* denemo wants a chord with no notes */
        DenemoObject *mud = newchord( $2.t1.a, $2.t1.b,0);

        mud->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
$$ = g_list_append(NULL,mud); }
        | SKIPNAME optional_notemode_duration           {
        /* denemo wants a chord with no notes */
        DenemoObject *mud = newchord( $2.t1.a, $2.t1.b,0);
        mud->type = SKIPNAME;
        mud->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
$$ = g_list_append(NULL,mud); }
        | MULTI_MEASURE_REST optional_notemode_duration         {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Input i = THIS->pop_spot ();

                Skip_req * sk = new Skip_req;
                sk->set_mus_property ("duration", $2);
                Span_req *sp1 = new Span_req;
                Span_req *sp2 = new Span_req;
                sp1-> set_span_dir ( START);
                sp2-> set_span_dir ( STOP);
                SCM r = scm_makfrom0str ("rest");
                sp1->set_mus_property ("span-type", r);
                sp2->set_mus_property ("span-type", r);

                Request_chord * rqc1 = new Request_chord (SCM_EOL);
                rqc1->set_mus_property ("elements", scm_list_n (sp1, 
SCM_UNDEFINED));
                Request_chord * rqc2 = new Request_chord (SCM_EOL);
                rqc2->set_mus_property ("elements", scm_list_n (sk, 
SCM_UNDEFINED));
                Request_chord * rqc3 = new Request_chord (SCM_EOL);
                rqc3->set_mus_property ("elements", scm_list_n (sp2, 
SCM_UNDEFINED));

                SCM ms = scm_list_n (rqc1, rqc2, rqc3, SCM_UNDEFINED);

                $$ = new Sequential_music (SCM_EOL);
                $$->set_mus_property ("elements", ms);
#endif
        }
        | STRING_ optional_notemode_duration    {
                DenemoObject *mud = newlyric($2.t1.a, $2.t1.b, $1.gstr->str);
                mud->user_string = $1.user_string;
                mud->type = LYRICS;
                stradd((*mud),$2);
                $$ = g_list_append(NULL,mud);
        }
        | chord {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Input i = THIS->pop_spot ();

                if (!THIS->lexer_->chord_state_b ())
                        THIS->parser_error (_ ("Have to be in Chord mode for 
chords"));
                $$ = $1;
#endif
        }
        ;

simul_open: DOUBLE_ANGLE_OPEN
        ;

simul_close: DOUBLE_ANGLE_CLOSE
        ;


chord:
        steno_tonic_pitch optional_notemode_duration chord_additions 
chord_subtractions chord_inversion chord_bass {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = Chord::get_chord ($1, $3, $4, $5, $6, $2);
                $$->set_spot (THIS->here_input ());
#endif
        };

chord_additions: {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = SCM_EOL;
#endif
} | CHORD_COLON chord_notes {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $2;
#endif
        }
        ;

chord_notes:
        chord_step {
                $$ = $1;
        }
        | chord_notes '.' chord_step {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_append2 ($$, $3);
#endif
        }
        ;

chord_subtractions: {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = SCM_EOL;
#endif
} | CHORD_CARET chord_notes {
                $$ = $2;
        }
        ;


chord_inversion:
        {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = SCM_EOL;
#endif
        }
        | '/' steno_tonic_pitch {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $2;
#endif
        }
        ;

chord_bass:
        {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = SCM_EOL;
#endif
        }
        | CHORD_BASS steno_tonic_pitch {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $2;
#endif
        }
        ;

chord_step:
        chord_note {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_cons ($1, SCM_EOL);
#endif
        }
        | CHORDMODIFIER_PITCH {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_cons (unsmob_pitch ($1)->smobbed_copy (), SCM_EOL);
#endif
        }
        | CHORDMODIFIER_PITCH chord_note {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
         /* Ugh. */
                $$ = scm_list_n (unsmob_pitch ($1)->smobbed_copy (),
                        $2, SCM_UNDEFINED);
#endif
        }
        ;

chord_note:
        bare_unsigned {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                 Pitch m;
                m.notename_ = ($1 - 1) % 7;
                m.octave_ = $1 > 7 ? 1 : 0;
                m.alteration_ = 0;

                $$ = m.smobbed_copy ();
#endif
} | bare_unsigned '+' {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Pitch m;
                m.notename_ = ($1 - 1) % 7;
                m.octave_ = $1 > 7 ? 1 : 0;
                m.alteration_ = 1;


                $$ = m.smobbed_copy ();
#endif
        }
        | bare_unsigned CHORD_MINUS {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                Pitch m;
                m.notename_ = ($1 - 1) % 7;
                m.octave_ = $1 > 7 ? 1 : 0;
                m.alteration_ = -1;

                $$ = m.smobbed_copy ();
#endif
        }
        ;

/*
        UTILITIES
 */
number_expression:
        number_expression '+' number_term {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_sum ($1, $3);
#endif
        }
        | number_expression '-' number_term {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_difference ($1, $3);
#endif
        }
| number_term ;

number_term:
        number_factor {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
#endif
        }
        | number_factor '*' number_factor {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_product ($1, $3);
#endif
        }
        | number_factor '/' number_factor {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_divide ($1, $3);
#endif
        }
        ;

number_factor:
        '(' number_expression ')'       {
                $$ = $2;
        }
        | '-'  number_factor { /* %prec UNARY_MINUS */
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_difference ($2, SCM_UNDEFINED);
#endif
        }
        | bare_number
        ;


bare_number:
        UNSIGNED        {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_int2scm ($1);
#endif
        }
        | REAL          {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
#endif
        }
        | NUMBER_IDENTIFIER             {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
#endif
        }
        | REAL CM_T     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_double2scm (gh_scm2double ($1) CM );
#endif
        }
        | REAL PT_T     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_double2scm (gh_scm2double ($1) PT);
#endif
        }
        | REAL IN_T     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_double2scm (gh_scm2double ($1) INCH);
#endif
        }
        | REAL MM_T     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_double2scm (gh_scm2double ($1) MM);
#endif
        }
        | REAL CHAR_T   {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = gh_double2scm (gh_scm2double ($1) CHAR);
#endif
        }
        ;


bare_unsigned:
        UNSIGNED {
                        $$ = $1;
        }
        | DIGIT {
                $$ = $1;
        }
        ;

bare_int:
        bare_number {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                if (scm_integer_p ($1) == SCM_BOOL_T)
                {
                        int k = gh_scm2int ($1);
                        $$ = k;
                } else
                {
                        THIS->parser_error (_ ("need integer number arg"));
                        $$ = 0;
                }
#endif
        }
        | '-' bare_int {
                $$.i = -$2.i;
        }
        ;


string:
        STRING_         {
                $$ = $1;
        }
        | STRING_IDENTIFIER     {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = $1;
#endif
        }
        | string '+' string {
LATER_MESSAGE(@$.first_line);
#ifdef LATER
                $$ = scm_string_append (scm_list_n ($1, $3, SCM_UNDEFINED));
#endif
        }
        ;


exclamations:
        /* empty */     { $$.i = 0; }
| exclamations '!' { if($1.i == 0){
                        $$.i = 1;
                        $$.user_string = $2.user_string;
                } else {
                        $$.user_string = g_strconcat($1.user_string, 
$2.user_string, NULL);/* FIXME memory leak $2*/
                                
                        $$.i = $1.i+1;
                 }
        }
        ;

questions:
        /* empty */     { $$.i = 0; }
| questions '?' { if($1.i == 0){
                        $$.i = 1;
                        $$.user_string = $2.user_string;
                } else {
                        $$.user_string = g_strconcat($1.user_string, 
$2.user_string, NULL);/* FIXME memory leak $2*/
                                
                        $$.i = $1.i+1;
                 }
        }
        ;


%%

#if GTK_MAJOR_VERSION <= 1
#define gtk_window_present(a)
#endif


static objnode *
use_up_ticks (objnode * h, gint ticks)
{
  DenemoObject *figmud = (DenemoObject *) h->data;
  while (ticks > 0)
    {
      h = h->next;
      figmud = h ? (DenemoObject *) h->data : NULL;
      if (!figmud)
        {
          g_warning ("Insufficient figures for bass part");
          return NULL;
        }
      ticks -= figmud->basic_durinticks;
    }
  return h;
}

static gboolean
create_figures (GList * b, GList * f)
{
  objnode *h = (objnode *) f->data;
  DenemoObject *figmud;

  for (figmud = (DenemoObject *) h->data; figmud;
       h = h->next, figmud = h ? (DenemoObject *) h->data : NULL)
    {
      if (figmud->type == CHORD)
        return FALSE;           /* there are already figures present */
    }


  for (; b && b->data; b = b->next)
    {
      objnode *g = (objnode *) b->data;
      DenemoObject *mud;
      for (mud = (DenemoObject *) g->data; mud;
           g = g->next, mud = (g ? (DenemoObject *) g->data : NULL))
        {
          if (mud->type == CHORD)
            {

            }

          if (mud->type == TUPOPEN || mud->type == TUPCLOSE
              || mud->type == GRACE_START || mud->type == GRACE_END)
            {
              DenemoObject *figmud =
                (DenemoObject *) g_malloc0 (sizeof (DenemoObject));
              memcpy (figmud, mud, sizeof (DenemoObject));
              f->data = g_list_append ((GList *) f->data, figmud);
            }
        }
      f = (f->next ? f->next : g_list_append (f, NULL));
    }

  return TRUE;
}

/* return next objnode of CHORD (is_figure will be true for h) type */
static objnode *
next_figure (objnode * h)
{
  DenemoObject *figmud;
  for (figmud = (DenemoObject *) h->data; figmud;
       h = h->next, figmud = h ? (DenemoObject *) h->data : NULL)
    {
      if (figmud->type == CHORD)
        return h;
    }
  g_assert (h == NULL);
  return NULL;
}

/* Link each chord in BASS to a figure (CHORD) or sequence of them in
FIGURES; create blank figures for each note present in BASS if none is present 
in
FIGURES, abort if incomplete figures present */
static void
fill_in_figures (DenemoStaff * bass, DenemoStaff * figures)
{
  GList *b = bass->measures, *f = figures->measures;
  /* if there are no figures create a set cloning the durations from bass */
  if (create_figures (b, f))
    return;

  for (b = bass->measures, f = figures->measures;
       b && b->data; b = b->next, f = f->next)
    {
      objnode *g = (objnode *) b->data;
      objnode *h = (objnode *) f->data;
      DenemoObject *mud;
      for (mud = (DenemoObject *) g->data; mud;
           g = g->next, mud = (g ? (DenemoObject *) g->data : NULL))
        {

          if (mud->type == CHORD)
            {
              if (h)
                {
/* set figure of bass chord to the first CHORD (ie figure) * object in the FIGURES list starting at h.
                   * Then move h on over other CHORDs (ie figures) if needed to
                   * use up the duration of the mud->object.
* If the list h has run out, abort since we don't know how * much is missing or why */ h = (objnode *) (((chord *) mud->object)->figure); (((chord *) mud->object)->figure) = next_figure (h);
                  if (h)
                    h =
                      use_up_ticks (h,
                                    mud->basic_durinticks -
                                    ((DenemoObject *) h->data)->
                                    basic_durinticks);
                }               /* if h */

              if (((chord *) mud->object)->figure == NULL)        /* not yet 
fixed up this CHORD in BASS */
                {
                  lyerror
                    ("Figures are incomplete for Bass - delete the figures or 
complete them!");
                }
              h = h ? h->next : NULL;
            }                   /* if mud is CHORD */
        }                       /* end of for each DenemoObject in the measure 
*/
    }                           /* for each measure */
}

/* create the list of measurewidths for si, add measures to short staffs and
   run set_initial_staffcontexts() to setup the clef etc in the staff structures
   Fill in any figured bass staff that is short of figures with blank figures.
 */
static void
fixup_measure_widths_and_contexts (DenemoScore * si)
{
  GList *g = si->thescore;
  DenemoStaff *curstaffstruct = staffstruct (g);
  int i, num_measures = 0;
  /* find num_measures of longest staff */
  for (g = si->thescore; g; g = g->next)
    {
      curstaffstruct = staffstruct (g);
      i = g_list_length (curstaffstruct->measures);
      if (num_measures < i)
        num_measures = i;
    }
  /* add measures to short staffs */
  for (g = si->thescore; g; g = g->next)
    {
      curstaffstruct = staffstruct (g);
      i = g_list_length (curstaffstruct->measures);
      while ((num_measures - i++) > 0)
        curstaffstruct->measures =
          g_list_append (curstaffstruct->measures, NULL);
    }
/*      if(g_list_length(si->measurewidths)) lyerror("si->measurewidths should be 
zero at this point");*/
  g = si->thescore;
  curstaffstruct = staffstruct (g);
  i = g_list_length (curstaffstruct->measures);
  while (i--)
    si->measurewidths =
      g_list_append (si->measurewidths, GINT_TO_POINTER (si->measurewidth));
  if (si->has_figures)
    fill_in_figures (si->has_figures->main_staff,
                     si->has_figures->related_staff);
  set_initial_staffcontexts (si);
  find_leftmost_allcontexts (si);
}


/* these scheme identifiers are difficult to track down in lilypond's source
tree: \major and \minor in particular I haven't found. There are a group in
lilypond/ly/script-init.ly, and clearly the file being parsed could define
additional ones, in general we just need to recognize them and pass on the
user_string, except where denemo has been equipped to represent them 
graphically.
In this last case we store a token to be returned as the lyval->type, the only
case where this is not the same as the token the lexer returns.
*/

static void
insert_scm (int type, gchar * str)
{
  nodegstr *n = (nodegstr *) g_malloc0 (sizeof (nodegstr));
  n->type = type;
  n->gstr = g_string_new (str);
  g_hash_table_insert (scm_identifiers, (gpointer) str, (gpointer) n);
}


static void
initialize_scm_identifiers (void)
{
  if (scm_identifiers)
    return;
  scm_identifiers = g_hash_table_new (g_str_hash, g_str_equal);
  insert_scm (MUSICMODE, "major");
  insert_scm (MUSICMODE, "minor");

  insert_scm (TONEOPTION, "thumb");
  insert_scm (TONEOPTION, "accent");
  insert_scm (TONEOPTION, "marcato");
  insert_scm (TONEOPTION, "staccatissimo");
  insert_scm (TONEOPTION, "portato");
  insert_scm (TONEOPTION, "fermata");
  insert_scm (TONEOPTION, "stopped");
  insert_scm (TONEOPTION, "staccato");
  insert_scm (TONEOPTION, "tenuto");
  insert_scm (TONEOPTION, "upbow");
  insert_scm (TONEOPTION, "downbow");
  insert_scm (TONEOPTION, "lheel");
  insert_scm (TONEOPTION, "rheel");
  insert_scm (TONEOPTION, "ltoe");
  insert_scm (TONEOPTION, "rtoe");
  insert_scm (TONEOPTION, "turn");
  insert_scm (TONEOPTION, "open");
  insert_scm (TONEOPTION, "flageolet");
  insert_scm (TONEOPTION, "reverseturn");
  insert_scm (TONEOPTION, "trill");
  insert_scm (TONEOPTION, "prall");
  insert_scm (TONEOPTION, "mordent");
  insert_scm (TONEOPTION, "upmordent");
  insert_scm (TONEOPTION, "downmordent");
  insert_scm (TONEOPTION, "prallprall");
  insert_scm (TONEOPTION, "prallup");
  insert_scm (TONEOPTION, "pralldown");
  insert_scm (TONEOPTION, "lineprall");
  insert_scm (TONEOPTION, "prallmordent");
  insert_scm (TONEOPTION, "upprall");
  insert_scm (TONEOPTION, "downprall");
  insert_scm (TONEOPTION, "segno");
  insert_scm (TONEOPTION, "coda");



  insert_scm (DYNAMICMARK, "ppp");
  insert_scm (DYNAMICMARK, "pp");
  insert_scm (DYNAMICMARK, "p");
  insert_scm (DYNAMICMARK, "mp");
  insert_scm (DYNAMICMARK, "mf");
  insert_scm (DYNAMICMARK, "f");
  insert_scm (DYNAMICMARK, "ff");
  insert_scm (DYNAMICMARK, "fff");
  insert_scm (DYNAMICMARK, "fff");
  insert_scm (DYNAMICMARK, "fp");
  insert_scm (DYNAMICMARK, "sf");
  insert_scm (DYNAMICMARK, "sff");
  insert_scm (DYNAMICMARK, "sp");
  insert_scm (DYNAMICMARK, "spp");
  insert_scm (DYNAMICMARK, "sfz");
  insert_scm (DYNAMICMARK, "rfz");

  insert_scm (DYNAMICMARK, "cr");
  insert_scm (DYNAMICMARK, "rc");

  insert_scm (DYNAMICMARK, "decr");
  insert_scm (DYNAMICMARK, "rced");



}


/* set parser_error_linenum
   set parser_error_message, linenum .
   if EDITOR environment variable not set
   show a dialog giving TEXT and and offering to exit application
   or return (for editing the whole file in gui).  */

void
parser_error (gchar * text, int line_number)
{

  GtkWidget *label;
  GtkWidget *editbutton;
  GtkWidget *exitbutton;
  if( (parser_error_message == NULL) ) {
        parser_error_message = strdup(text);
        parser_error_linenum = line_number;
  }

  if (!getenv ("EDITOR"))
    {
      if (!parser_error_dialog)
        {
        /*warningdialog("Unable to load this file - quitting");*/
        return(-1);
        }
    }
  else
    {
      g_print ("\nAssociated message: %s at line %d\n", text, line_number);

    }

}



/* called by lexer at EOF */
void set_trailing_white_space (gchar *trailing) {
        trailing_white_space = g_strdup (trailing);
}

static void attach_trailing_white_space (GList *top) {
        GList *g = g_list_last (top);
        u_str(g) = g_strconcat(u_str(g), trailing_white_space,NULL);
        g_free(trailing_white_space);
        trailing_white_space = NULL;
}

char *
header_str(char *key)
{
        nodeglist *x;
        char *pt = NULL;
        int n;

         x = (nodeglist *) g_hash_table_lookup (name_value_pairs, key);
        if (x)
        {
            pt = u_str(x->branch);
            if (*pt == '"') pt++;
            n = strlen(pt) - 1;
            if (*(pt+n) == '"') *(pt+n) = 0;
        }
        return(pt);
}
static void
score_prop_from_lily (DenemoGUI *gui)
{
DenemoScore *si = gui->si;   
    char *pt;
    GList *scm;

    if ((pt = header_str("midi_tempo")))
    {
        si->tempo = atoi(pt);
    }
    if ((pt = header_str("lilyversion")))
    {
        gui->lilycontrol.lilyversion = g_string_new(pt);
    }
    if (findtok (lily_file, HEADER))
    {
        if ((pt = header_str ("title")))
            g_string_assign (si->headerinfo.title, pt);
        if ((pt = header_str ("subtitle")))
            g_string_assign (si->headerinfo.subtitle, pt);
        if ((pt = header_str ("poet")))
            g_string_assign (si->headerinfo.poet, pt);
        if ((pt = header_str ("composer")))
            g_string_assign (si->headerinfo.composer, pt);
        if ((pt = header_str ("meter")))
            g_string_assign (si->headerinfo.meter, pt);
        if ((pt = header_str ("opus")))
            g_string_assign (si->headerinfo.opus, pt);
        if ((pt = header_str ("arranger")))
            g_string_assign (si->headerinfo.arranger, pt);
        if ((pt = header_str ("instrument")))
            g_string_assign (si->headerinfo.instrument, pt);
        if ((pt = header_str ("dedication")))
            g_string_assign (si->headerinfo.dedication, pt);
        if ((pt = header_str ("piece")))
            g_string_assign (si->headerinfo.piece, pt);
        if ((pt = header_str ("head")))
            g_string_assign (si->headerinfo.head, pt);
        if ((pt = header_str ("copyright")))
            g_string_assign (si->headerinfo.copyright, pt);
        if ((pt = header_str ("footer")))
            g_string_assign (si->headerinfo.footer, pt);
        if ((pt = header_str ("tagline")))
            g_string_assign (si->headerinfo.tagline, pt);
    }
    for(scm = findtok (lily_file, SCM_T); scm ; scm = scm->next)
    {
        if ((pt = strstr (u_str (scm), "set-global-staff-size")))
        {
                int font;
                if (sscanf (pt+21, " %d", &font) == 1)
                {
                        gui->lilycontrol.fontsize = font;
                }
                else
                        g_warning("%s no font", pt);
        }
        if ((pt = strstr (u_str (scm), "set-default-paper-size")))
        {
            char *tmp;
            char *pt2 = strchr(pt, '\"');
            if (pt2)
            {
                tmp = g_strdup(pt2+1);
                pt2 = strchr(tmp, '\"');
                if (pt2)
                {
                    *pt2 = 0;
        //          g_print ("Paper size %s\n", tmp);
                    g_string_assign (gui->lilycontrol.papersize, tmp);
                    *tmp = 0;
                    g_free (tmp);
                }
            }
            if (!pt2)
                g_warning("%s paper size error", pt);
        }
    }
}


/* from denemo's easylyparser.y
   note that this function generates a list
   of DenemoScore structures (one for each \score{} block
   in the lilypond file, returning the current one (as set in
   si->theFile->current_scoreblock). This rather clumsy
   arrangement is historical from when si was the root data
   structure: see denemo.h      
 */
int
lyinput (gchar * filename, DenemoGUI *gui)
{
  FILE *lyin;
  GList *score_block_list = NULL;
  initialize_scm_identifiers ();
  name_value_pairs = g_hash_table_new (g_str_hash, g_str_equal);/* FIXME memory 
leak */
  default_duration_.t1.a = 2;
  default_duration_.t1.b = 0;
init_crescendo_state();
  DenemoScore *si = gui->si;

  while (1)
    {                           /* keep trying to open the file */
      if ((lyin = fopen (filename, "r")) == NULL)
        {
          fprintf (stderr, "Cannot open the file: %s\n", filename);
          return -1;
        }
      else
        {                       /* file is opened */
        /* any old lily parse tree (ie si->lily_file) is already demolished */
          lily_file = NULL;     
          parser_error_dialog = NULL;
          parser_error_message = NULL;
/* free_score (si); CHANGE THIS TO FREE ALL THE si in si->thefile->currentDenemoScore list... */


          // Calling init_score here would cause piece in header to set to "Movement 
2"
          si->measurewidths = NULL;
          si->thescore = NULL;
          /* in case we are re-entering via reload after error */
          lyrestart (lyin);     

          lylineno = 1;         /* not done by lexer for some reason */
          push_note_state ();

          while (!feof (lyin))
            {
              lyparse ();
if (parser_error_message) { fprintf(stderr,"%s\n", parser_error_message);
        warningdialog("Unable to cope with this file");
        return -1;
}
            }
          if (parser_error_message == NULL)
            {
              GList *score = findtok (lily_file, SCORE);
              if (score)
              {
                if (create_score_from_lily (si, br (score)) == 0)
                  {
                    GList *top;
                    nodemin *n = (nodemin *) g_malloc0 (sizeof (nodemin));
                    score_prop_from_lily(gui);

                    while (score && score->next)
                      {
                        score = findtok (score->next, SCORE);
                        if (score)
                          {
                            DenemoScore *nextsi =
                              (DenemoScore *) g_malloc0 (sizeof (DenemoScore));
                            init_score (nextsi, gui);
                            create_score_from_lily (nextsi, br (score));
                            score_block_list =
                              g_list_append (score_block_list, nextsi);
                          }
                      }

                    n->type = TEXT;
                    /* must be g_free-able pointer */
                    n->user_string = g_strdup ("");        
                    top = g_list_append (NULL, n);
                    /* simplify the tree into TEXT and DENEMO_MEASURES nodes */
#ifdef LILYEDIT
                    lily_write_out (top, lily_file, TO_NODE);   
#endif
                    attach_trailing_white_space (top);
                    si->lily_file = top;
                    si->lily_file = NULL; // stop lilypond file edit 
restrictions- RRR
                    fixup_measure_widths_and_contexts (si);
#ifdef LILYEDIT
                    /* write out the text to editable display */
                    create_text_display (gui);  
#if GTK_MAJOR_VERSION > 1
/* so that the following toggle starts the scorearea display */
                    gtk_widget_hide (si->scorearea); 
                    toggle_top_node (NULL, gui);
/* present denemo's graphical window first */
                    gtk_window_present ((GtkWindow *) gui->window);  
#endif

                    if (g_list_length (score_block_list) > 0)
                      {
                        GList *g;
                        DenemoScore *nextsi;
                        for (g = score_block_list; g; g = g->next)
                          {
                            nextsi = (DenemoScore *) g->data;
                            fixup_measure_widths_and_contexts (nextsi);
                            nextsi->window = si->window;
                            nextsi->scorearea = si->scorearea;
                            nextsi->pixmap = si->pixmap;
                            nextsi->vadjustment = si->vadjustment;
                            nextsi->vscrollbar = si->vscrollbar;
                            nextsi->hadjustment = si->hadjustment;
                            nextsi->hscrollbar = si->hscrollbar;
                            nextsi->menubar = si->menubar;
                            nextsi->textbuffer = si->textbuffer;
                            nextsi->textwindow = si->textwindow;
                            nextsi->textview = si->textview;
                            nextsi->musicdatabutton = si->musicdatabutton;
                            nextsi->sigid = si->sigid;
                            nextsi->curlilynode = si->curlilynode;
                            nextsi->lily_file = si->lily_file;
                            nextsi->prefs = si->prefs;
/* certain fields are not really score specific - they are file specific - 
share these */

/* but we have no way of sharing has_changed yet FIXmME */

                          }

                        /* append a copy of the score block si (the one that 
the display is hardwired to)
                           to the list of score_blocks, so that the display can 
be cycled around by copying
                           from score_block_list to si  */
                        nextsi = (DenemoScore *) g_malloc0 (sizeof 
(DenemoScore));
                        memcpy (nextsi, si, sizeof (DenemoScore));
                        score_block_list =
                          g_list_append (score_block_list, nextsi);
                        si->scoreblocks = score_block_list;
                      }

#endif // LILYEDIT


                    return 0;
                  }
            }                   /* if successful lily parse */
            else
            {
fprintf(stderr, "Parse Error Line %d: %s\n", parser_error_linenum, parser_error_message);
                return(1);
            }
          }

        }
      reset_initial_lexer_state ();
      g_assert (lily_file);
#ifdef LILYEDIT
      if (!getenv ("EDITOR"))
        {
          long len;
          gchar *filecontents;
          fseek (lyin, 0L, SEEK_END);
          len = ftell (lyin);
          filecontents = (gchar *) g_malloc (len + 1);  /* +1 for NULL 
terminator */
          rewind (lyin);
          fread (filecontents, len, 1, lyin);
          *(filecontents + len) = '\0';
          fclose (lyin);
          u_str (lily_file) = filecontents;
          lily_file->next = NULL;
          si->lily_file = lily_file;
          si->curlilynode = lily_file;
          if (si->textwindow)
            lily_text_change (si);
          else
            create_text_display (si);
          gui->filename = g_string_new (filename);
          gtk_widget_set_sensitive (si->textview, TRUE);
          gtk_widget_set_sensitive (si->scorearea, FALSE);

          /* put dialog on top - it would be nice to use gtk_dialog_run() but  
how do you use it? */
          if (parser_error_dialog)
            gtk_window_present ((GtkWindow *) parser_error_dialog);
          return -1;
        }
      else
        {                       /* use external editor */
          GString *cmd;
          int ret;
          fclose (lyin);
          cmd = g_string_new (getenv ("EDITOR"));
          g_string_append_printf (cmd, " +%d %s", parser_error_linenum,
                                  filename);
          g_warning
            ("Using environment variable $EDITOR calling system with %s\n",
             cmd->str);
          ret = system (cmd->str);
#if 0
          if (WIFIGNALED (ret) &&
              (WTERMSIG (ret) == SIGINT || WTERMSIG (ret) == SIGQUIT))
            break;
#endif
          if (ret)
            break;
        }
#endif //LILYEDIT
        lyerror ("File load failed\n");
return -1; } /* forever */
  lyerror ("File load failed\n");
  return -1;                    /* there is no handler for this yet - never has 
been! */
}
#ifdef YYPRINT

static gchar *
type_name(gint type)
{
        gint i;
        for(i=0; yytoknum[i] != type; i++)
        {
            if (i > YYNTOKENS)
                return("");
        }
        return yytname[i];
}

#endif





reply via email to

[Prev in Thread] Current Thread [Next in Thread]