%{ // -*-Fundamental-*-

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

  source file of the GNU LilyPond music typesetter

  (c)  1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
           Jan Nieuwenhuizen <janneke@gnu.org>
	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; /*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> LILYDIRECTIVE_TOKEN


%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 <generic> 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)  {
		  lily_file = g_list_concat (lily_file, $2);
//list_tree("In parser toplevel", lily_file);
		} else {
		lily_file = $2;
		}
	}
	| lilypond assignment { 
		if(lily_file)  {
		 	 lily_file= 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 	{
	$$ = $1;
	lyerror ("parser should have caught this");
		/*intercept this at lexical level*/	
	}
	| score_body output_def {
	$$ = $1;
	lyerror ("parser should have caught this");
		/*intercept this at lexical level*/
	}
	| score_body error {
	$$ = $1;
	lyerror("score_body error");
	}
	;


/*
	MIDI
*/
output_def:
	music_output_def_body '}' {
	//	MALLOC_NODE(n2, $2)
	//	$$ = g_list_append($1, n2);
	$$ = NULL;
		
/*		THIS-> lexer_-> scopes_.pop ();*/
	}
	;


music_output_def_body:
	MIDI '{' tempo_optional	{
	   //     $$ = NULL;
	//	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; }
	| LILYDIRECTIVE_TOKEN {
		DenemoObject *mud = lily_directive_new ($1.user_string);		
		$$ = g_list_append(NULL,mud);
	}
	| LILYDIRECTIVE_TOKEN STRING_ {
		DenemoObject *mud = lily_directive_new (g_strconcat($1.user_string, $2.user_string, NULL));	
		$$ = g_list_append(NULL,mud);
	}
	| 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 '=' 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 {
	DenemoObject *mud = lily_directive_new ("[");	
		$$ = g_list_append(NULL,mud);
	}
	| E_RIGHTSQUARE {
	DenemoObject *mud = lily_directive_new ("]");	
		$$ = g_list_append(NULL,mud);
	}
	| 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_  	{
	                 DenemoObject *mud = lily_directive_new (g_strconcat($1.user_string, $2.user_string, NULL));
		         $$ = g_list_append(NULL,mud);

			//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;
	}
	| '['		{
	DenemoObject *mud = lily_directive_new ("[");	
		$$ = g_list_append(NULL,mud);

	}
	| ']'		{
	DenemoObject *mud = lily_directive_new ("]");	
		$$ = g_list_append(NULL,mud);	

	}
	| 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 {
	$$ = $1;
	}
	;
gen_text_def:
        full_markup {

	DenemoObject *mud = lily_directive_new ($1.user_string);	
		$$ = g_list_append(NULL,mud);
        }
/* 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 = CHORD;
	mud->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
	$$ = g_list_append(NULL,mud); 
	}
	| MULTI_MEASURE_REST optional_notemode_duration  	{
	/* treat as skip for the moment */
	DenemoObject *mud = newchord( $2.t1.a, $2.t1.b,0);
	mud->type = CHORD;
	mud->user_string = g_strconcat($1.user_string, $2.user_string, NULL);
	$$ = g_list_append(NULL,mud); 

	}
	| STRING_ optional_notemode_duration 	{
		DenemoObject *mud = NULL;//newlyric($2.t1.a, $2.t1.b, $1.gstr->str);
	if(mud) {
		mud->user_string = $1.user_string;
	        mud->type = LYRICS;
		stradd((*mud),$2);
		$$ = g_list_append(NULL,mud);
                }	else {
		DenemoObject *mud = lily_directive_new (g_strconcat($1.user_string, $2.user_string, NULL));	
		$$ = 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 gint
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);
  if(set_initial_staffcontexts (si))
	return -1;
  find_leftmost_allcontexts (si);
  return 0;
}


/* 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;
	}
    }
  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 0
	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);
#else
//FIXME create tagged directives for this information
#endif


    }
    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.staffsize = g_string_new(g_strdup_printf("%d", 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;
  endcontextnode.type = endcontext; /*the only instantiation of this
node, used as a marker */
  init_crescendo_state();
  DenemoScore *si = gui->si;

 //init_score (si, gui);
//	si->thescore = NULL;
  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;
	  // 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 ();
	    }
	 
	    {
	      GList *score = findtok (lily_file, SCORE);
	      if (score)
		if (create_score_from_lily (si, br (score)) == 0)
		  {
		    GList *top;
		    score_prop_from_lily(gui);
		    if(fixup_measure_widths_and_contexts (si))
			goto error;
		    while (score && score->next)
		      {
			score = findtok (score->next, SCORE);
			if (score)
			  {
	                    insert_movement_after(NULL, NULL);
			    DenemoScore *nextsi =
			      Denemo.gui->si;
			    init_score (nextsi, gui);
		 	    create_score_from_lily (nextsi, br (score));
		            if(fixup_measure_widths_and_contexts (nextsi))
				goto error;
			    
			  }
		      }

		    return 0;
		  }
	    }

	}
      reset_initial_lexer_state ();
error:
    //  if(si->thescore==NULL) {
	deletescore(NULL, Denemo.gui);
        open_user_default_template(REPLACE_SCORE);
    //  }	
      if (lily_file == NULL)
		return 1;

        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 (gchar*)yytname[i];
}

#endif
