/* phase1.c -- Phase 1 of adagio compilation...	 */ 
/*
 * this module parses adagio programs and builds a linked list structure
 * consisting of notes and control changes in time order.
 */

/*****************************************************************************
*	    Change Log
*  Date	    | Change
*-----------+-----------------------------------------------------------------
* 31-Dec-85 | Created changelog
* 31-Dec-85 | Add c:\ to include directives
* 31-Dec-85 | Added standard command scanner, metronome variable, need to add 
*	    | cmdline_help procedure
* 31-Dec-85 | Call intr_init
* 31-Dec-85 | Set musictrace from command line via -trace
* 31-Dec-85 | Added -poll
*  1-Jan-86 | Put error messages out to stderr
*  1-Jan-86 | Set IsAT.	 Can be later overridden by -at and -xt switches,
*	    | currently only used for diagnostics (may be needed for
*	    | compatibles, who knows?  In which case remove the tests which
*	    | confirm the type of processor)
*  1-Jan-86 | <rgd/jmn> Removed dur-adjusted message
*  1-Jan-86 | Added miditrace
* 18-Jan-86 | Shortened durations by 1/200 s to avoid roundoff problems --
*	    | see buildnote for details.
*  3-Mar-86 | Allow octave and accidentals in either order after pitch name.
*	    | Default octave is now one that gets nearest previous pitch,
*	    |  the tritone (half an octave) interval is descending by default.
*	    | Special commands handled by table search, !Rate command added
*	    |  to scale all times by a percentage (50 = half speed).
*  9-Mar-86 | Use space to limit amount of storage allocation.	Otherwise
*	    |	exhausting storage in phase1 caused phase2 to fail.
* 12-Mar-86 | Broke off command line parser into adagio.c, only parser remains
* 24-Mar-86 | Changed representation from note_struct to event_struct
*	    | Parse M, N, O, X, and Y as control change commands
* 23-May-86 | Added , and ; syntax: "," means "N0\n", ";" means "\n"
* 16-Jul-86 | modify to only call toupper/lower with upper/lower case as
*	    |  parameter to be compatible with standard C functions
*  7-Aug-86 | fixed bug with default pitches and rests
*****************************************************************************/

#include "cext.h"
#include "stdio.h"
#include "ctype.h"
#include "malloc.h"
#include "adagio.h"
#include "cmdline.h"
#include "phase1.h"

extern long space;	/* remaining free bytes */

/****************************************************************************
* The following are used to simulate fixed point with the radix point
* 8 bits from the right:
****************************************************************************/
#define unity 256
#define round(x) (((x)+128)>>8)
#define precise(x) ((x)<<8)

#define nullstring(s) (s[0] == 0)

/****************************************************************************
* Routines local to this module:
****************************************************************************/
private	     event_type ctrlalloc();
private		   void	do_a_rest();
private		   void	doabsdur();
private		   void	doabspitch();
private		   void	docomment();
private		   void	doctrl();
private		   void	dodur();
private		   void	doerror();
private		   void	doloud();
private		   void	donextdur();
private		   void	dopitch();
private		   void	doprogram();
private		   void	dorate();
private		   void	dospecial();
private		   void	dotempo();
private		   void	dotime();
private		   void	dovoice();
private		   void	fferror();
private		boolean init();
private		   void	ins_event();
private		boolean ins_ctrl();
private		boolean ins_note();
private		    int issymbol();
private		   void	marker();
private	     event_type nalloc();
private		   void	parseend();
private		   void	parsefield();
private		boolean parsenote();
private		   void	reverse();
private		    int scan();
private		    int scan1();
private		    int scanint();

/****************************************************************************
* data structures for parser lookup tables
****************************************************************************/

struct durt {	/* duration translation table */
    char symbol;
    long value;
};
struct durt durtable[5] = {
    'W', 240,
    'H', 120,
    'Q', 60,
    'I', 30,
    'S', 15
};

struct loudt {	/* loudness translation table */
    char symbol[4];
    int value;
};

struct loudt loudtable[] = {
    "PPP", 20,
    "PP\0", 26,
    "P\0\0", 34,
    "MP\0", 44,
    "MF\0", 58,
    "F\0\0", 75,
    "FF\0", 98,
    "FFF", 127
};

private char *ssymbols[] = {{"TEMPO"}, {"RATE"}};
			/* this was inside istempo, but */
			/* I moved it here because of a compiler bug */

#define sym_tempo 0
#define sym_rate 1
/* number of symbols */
#define sym_n 2

#define linesize 100
private char line[linesize];	/* the input line */
private char token[linesize];	/* a token scanned from the input line */

private boolean pitch_flag;	/* set when a pitch is indicated */
	/* (if controls changes are given, only allocate a note event if
	 *  a pitch was specified -- i.e. when pitch_flag is set)
	 */
private boolean rest_flag;	/* set when a rest (R) is found */
	/* this flag is NOT inherited by the next line */

private boolean symbolic_dur_flag;
		/* true if last dur was not absolute
		 * (if this is set, then the default duration is changed
		 *  accordingly when the tempo is changed.)
		 */

private boolean ctrlflag[nctrl];
		/* true if control change was present
		 * ctrlflag[0] true if ANY control change
		 * was present
		 */
private int ctrlval[nctrl];
		/* the new value of the control */

private int last_prog;	/* saved value of program from previous line */
	/* (this is needed to implement the rule that note
	 *  events are generated for rests if the program has changed.)
	 */

/****************************************************************************
*				state variables
* Because each line of an Adagio score inherits properties from the previous
* line, it makes sense to implement the parser as a collection of routines
* that make small changes to some global state.	 For example, pitch is a
* global variable.  When the field G4 is encountered, the dopitch routine
* assigns the pitch number for G4 to the variable pitch.  After all fields
* are processed, these variables describe the current note and contain the
* default parameters for the next note as well.
*
* Global variables that are used in this way by the parsing rountines are:
****************************************************************************/
private int
    linex,	/* index of the next character to be scanned */
    lineno,	/* current line number */
    fieldx,	/* index of the current character within a field */
    pitch,	/* pitch of note */
    loud,	/* loudness of note */
    voice,	/* voice (midi channel) of note */
    program;	/* midi program (timbre control) of note */

private boolean ndurp;		/* set when a next (N) is indicated */
	/* (next time defaults to the current time plus duration unless
	 *  overridden by a next (N) command whose presence is signalled
	 *  by ndurp.)
	 */

private long
    thetime,	/* the starting time of the note */
    rate,	/* time rate -- scales time and duration, default = 100 */
    ntime,	/* the starting time of the next note */
    dur,	/* the duration of the note */
    tempo,	/* the current tempo */
    start;	/* the reference time (time of last !tempo or !rate cmd) */

private int pitchtable[7] = { 57, 59, 48, 50, 52, 53, 55 };

extern char score_na[name_length];

private int note_count = 0;	/* the number of notes translated */
private int ctrl_count = 0;	/* ditto for control commands */

private int debug = false;	/* controls verbose printout */

/****************************************************************************
*				ctrlalloc
* Outputs: returns an event_type for representing a control change
*	   returns NULL if space is unavailable
* Effect: allocates ctrlsize bytes
* Assumes: space tells how many bytes are available
****************************************************************************/
private event_type ctrlalloc()
{	
    char *malloc();
    event_type result;
    space -= ctrlsize;
    if (space > 0) {
	result = (event_type ) malloc(ctrlsize);
	if (result == NULL)	/* this should never happen ... */
	    printf("Internal error: Out of memory, space = %ld.\n",space);
    } else result = NULL;
    return result;
}

/****************************************************************************
*				do_a_rest
* Effect: parses a rest (R) command
****************************************************************************/

private void do_a_rest()
{
    if (fieldx < strlen(token))
	fferror("Nothing expected after rest");
    rest_flag = true;
}

/****************************************************************************
*				doabsdur
* Effect: parses an absolute dur (U) command
****************************************************************************/

private void doabsdur()
{
    if (isdigit(token[fieldx])) {
	dur = precise( (long) scanint());
	dur = (dur * 100) / rate;
	if (fieldx < strlen(token)) {
	    fieldx = 2;
	    fferror("U must be followed by digits only");
	}
	symbolic_dur_flag = false;
    } else fferror("No digit after U");
}

/****************************************************************************
*				doabspitch
* Effect: parses an absolute pitch (P) command
****************************************************************************/

private void doabspitch()
{
    if (isdigit (token[fieldx])) {
	pitch = scanint();
	pitch_flag = true;
	if (fieldx < strlen(token))
	    fferror("P must be followed by digits only");
	else if (pitch < minpitch) {
	    fieldx = 1;
	    fferror("Minimum pitch of 12 will be used");
	    pitch = minpitch;
	} else if (pitch > maxpitch) {
	    fieldx = 1;
	    fferror("Maximum pitch of 115 will be used");
	    pitch = maxpitch;
	}
    } else fferror("No digits after P");
}

/****************************************************************************
*				docomment
* Effect: parses a comment (*) command
****************************************************************************/

private void docomment()
{
    line[linex] = 0; /* force end of line to skip comment line */
}

/****************************************************************************
*				doctrl
* Inputs:
*    n: control number
* Effect: parses a control (J, K, M, O, X, or Y) command
****************************************************************************/

private void doctrl(n)
    int n;
{
    ctrlval[n] = scanint();
    if (fieldx < strlen(token) ) {
	fferror("Only digits expected here");
    } else {
	ctrlflag[n] = true;
	ctrlflag[0] = true;	/* ctrlflag[0] set if any flag is set */
    }
}

/****************************************************************************
*				dodur
* Effect: parses a duration (S, I, Q, H, or W) command
****************************************************************************/

private void dodur()
{
    int i, dotcnt = 0;
    long dotfactor;

    for (i=0; i<=5; i++) {
	if (durtable[i].symbol == token[fieldx-1]) {
	    dur = precise(durtable[i].value);
	    break;
	}
    }
    if (i == 5) {
	fieldx--;
	fferror("Duration expected: one of W, H, Q, I, or S");
	return;
    }
    while (fieldx < strlen(token)) {
	if (token[fieldx] == 'T') {	/* triplet notation */
	    dur = (dur*2) / 3;
	    fieldx++;
	} else if (token[fieldx] == '.') {	/* dotted notation */
	    dotcnt++;
	    fieldx++;
	} else if (isdigit(token[fieldx])) {	/* numbers are multipliers */
	    dur = dur * (long) scanint();
	} else {
	    fferror("Bad duration");
	    fieldx = strlen(token) + 1;
	}
    }
    dotfactor = 1;
    for (i=1; i<=dotcnt; i++)
	dotfactor = dotfactor * 2;
    dur = (2 * dur) - (dur / dotfactor);
    dur = (dur * 100) / tempo;	/* time in centiseconds */
    dur = (dur * 100) / rate;
    symbolic_dur_flag = true;	/* see symbolic_dur_flag declaration */
}

/****************************************************************************
*				doerror
* Effect: parse an unrecognized field by reporting an error
****************************************************************************/

private void doerror()
{
    fieldx = 0;
    fferror("Bad field");
}

/****************************************************************************
*				doloud
* Effect: parse a loudness (L) command
****************************************************************************/

private void doloud()
{
    int i, j;

    if (strlen(token) <= 1) {
	fieldx = 0;
	fferror("L must be followed by loudness indication");
	return;
    }
    if (isdigit(token[fieldx])) {
	loud = scanint();
	if (fieldx < strlen(token))
	    fferror("Digits expected after L");
	else if (loud > 127) {
	    fieldx = 1;
	    fferror("Maximum loudness of 127 will be used");
	    loud = 127;
	}
	return;
    }
    if (strlen(token) > 4 ) {	/* maximum is 4, e.g. "Lppp" */
	fieldx = 0;
	fferror("Loudness field too long");
	return;
    }
    if (strlen(token) != 4) {	/* pad short symbols with 0	*/
	i = strlen(token);	/* e.g. "p\0" -> "p\0\0"	*/
	token[i+1] = '\0';
    }
    
    for (i = 0; i <= 7; i++) {	/* loop through possibilities	*/
	for (j = 0; j <= 2; j++) {	/* test 3 characters	*/
	    if (token[fieldx+j] != loudtable[i].symbol[j])
		break;
	}
	if (j == 3) {
	    loud = loudtable[i].value;
	    return;
	}
    }
    fieldx = 1;
    fferror("Bad loudness indication");
}

/****************************************************************************
*				donextdur
* Effect: parse a next (N) command
* Implementation:
*	The syntax is N followed by a duration, so save dur and use dodur()
*	to parse the duration field.  Then restore dur (what a hack!).
*	The form N<digits> is parsed directly with scanint().
****************************************************************************/

private void donextdur()
{
    long save;	/* save the current duration */
    
    ndurp = true;	/* flag that N was given */
    if (isdigit(token[fieldx])) {
	ntime = precise( (long) scanint());
	if (fieldx < strlen(token))
	    fferror("Only digits were expected here");
    } else {
	fieldx++;
	save = dur;	
	dodur();
	ntime = dur;	/* get the result from dur, */
	dur = save;	/* and then restore it	*/
    }
}

/****************************************************************************
*				dopitch
* Effect: parses a pitch command
****************************************************************************/

private void dopitch()
{
    int p, octave;
    int octflag = false;	/* set if octave is specified */

    p = pitchtable[token[0]-'A'];
    while (true) {
	if (token[fieldx] == 'S') {			       /* sharp */
	    p++;
	    fieldx++;
	} else if (token[fieldx] == 'N') {			/* skip */
	    fieldx++;
	} else if (token[fieldx] == 'F') {			/* flat */
	    p--;
	    fieldx++;
	} else if (isdigit(token[fieldx]) && !octflag) {      /* octave */
	    octave = scanint();
	    octflag = true;
	} else break;				   /* none of the above */
    }
    if (octflag) p = (p-48) + 12 * octave;  /* adjust p to given octave */
    else {		  /* adjust p to note nearest the default pitch */
	int octdiff = (p + 126 - pitch) / 12;
	p = p + 120 - (octdiff * 12);
    }
    if (fieldx != strlen(token))	    /* any unparsed characters? */
	fferror("Bad pitch indication");
    if (p > maxpitch) {				     /* pitch in range? */
	fieldx = 1;
	fferror("Pitch too high");
	p = maxpitch;
    }
    pitch = p;
    pitch_flag = true;
}

/****************************************************************************
*				doprogram
* Effect: parses a program change (Z) command
****************************************************************************/

private void doprogram()
{
    if (isdigit(token[fieldx])) {
	program = scanint();
	if (fieldx < strlen(token)) {
	    fferror("Z must be followed by digits only");
	} else if (program < minprogram) {
	    fieldx = 1;
	    fferror("Minimum program of 1 will be used");
	    program = minprogram;
	} else if (program > maxprogram) {
	    fieldx = 1;
	    fferror("Maximum program of 128 will be used");
	    program = maxprogram;
	}
    } else fferror("No digit after Z");
}

/****************************************************************************
*				dorate
* Effect: parses a !rate command
****************************************************************************/

private void dorate()
{
    linex += scan(&line[linex]);
    if (strlen(token) == 0)
	fferror("rate number expected");
    else {
	long oldrate = rate;
	fieldx = 0;
	rate = scanint();
	if (fieldx < strlen(token) )
	    fferror("Only digits expected here");
	if (rate == 0) {
	    fieldx = 0;
	    fferror("Rate 100 will be used here");
	    rate = 100;
	}
	start = thetime;
	/* adjust dur in case it is inherited by next note */
	    dur = (dur * oldrate);
	    dur = dur / rate;
    }
}

/****************************************************************************
*				dospecial
* Effect: parses special (those starting with "!") commands
****************************************************************************/

private void dospecial()
{
    switch (issymbol()) {
	case sym_tempo: dotempo();
	    break;
	case sym_rate: dorate();
	    break;
	default: fferror("Special command expected");
    }
    parseend(); /* flush the rest of the line */
}

/****************************************************************************
*				dotempo
* Effect: parses a !tempo command
****************************************************************************/

private void dotempo()
{
    linex += scan(&line[linex]);
    if (strlen(token) == 0)
	fferror("Tempo number expected");
    else {
	long oldtempo = tempo;
	fieldx = 0;
	tempo = scanint();
	if (fieldx < strlen(token))
	    fferror("Only digits expected here");
	if (tempo == 0) {
	    fieldx = 0;
	    fferror("Tempo 100 will be used here");
	    tempo = 100;
	}
	start = thetime;
	/* adjust dur in case it is inherited by next note */
	if (symbolic_dur_flag) {
	    dur = (dur * oldtempo);
	    dur = dur / tempo;
	}
    }
}

/****************************************************************************
*				dotime
* Effect: parses a time (T) command
* Implementation: see implementation of donextdur()
****************************************************************************/

private void dotime()
{
    int save;

    if (isdigit(token[fieldx])) {
	thetime = precise( (long) scanint());
	if (fieldx < strlen(token) )
	    fferror("Only digits were expected here");
	} else {
	    fieldx++;
	    save = dur; 
	    dodur(); 
	    thetime = dur;
	    dur = save;
	}
    thetime += start;	/* time is relative to start */
}

/****************************************************************************
*				dovoice
* Effect: parse a voice (V) command (the voice is the MIDI channel)
****************************************************************************/

private void dovoice()
{
    if (isdigit(token[fieldx])) {
	voice = scanint();
	if (fieldx < strlen(token))
	    fferror("V must be followed by digits only");
	if (voice > 16) {
	    fferror("number too high, using 16 instead");
	    voice = 16;
	} else if (voice < 1) {
	    fferror("number too low, using 1 instead");
	    voice = 1;
	}
    } else fferror("No digit after V");
}

/****************************************************************************
*				fferror
* Inputs:
*	char *s: an error message string
* Effect:
*	prints the line with the error
*	puts a cursor (^) at the error location
*	prints the error message (s)
* Implementation:
*	this routine prints a carat under the character that
*	was copied into token[fieldx].	E.g. if fieldx = 0, the
*	carat will point to the first character in the field.
****************************************************************************/

private void fferror(s)
char *s;
{
    fprintf(stderr, "%3d | ", lineno);
    fprintf(stderr, "%s", line);
    marker(linex-strlen(token)+fieldx+1+6);
    fprintf(stderr, "Error: %s.\n", s);
}

/****************************************************************************
*				init
* Outputs:	Returns true if OK, false on error.
* Effect:	Initializes program state.
****************************************************************************/

private boolean init()
{
    int i;

    debug = cl_switch("-print");

    for (i = 0; i < nctrl; i++) ctrlflag[i] = false;

    dur = precise ((long) 60); /* quarter note */
    lineno = 0;
    thetime = 0;
    pitch = 48;
    loud = 127;
    voice = 1;
    last_prog = program = 1;
    tempo = 100;
    rate = 100;
    start = 0;
    symbolic_dur_flag = true; /*scale dur by tempo*/
    return true;
}

/****************************************************************************
*				ins_ctrl
* Inputs:
*	event_type *score: a linked list in which to insert
* Outputs:
*	returns true on success, false on error (not enough space)
* Effect: 
*	control events corresponding to current line are inserted in score
* Implementation:
*	ctrlflag[i] is true if control i was specified in this line, so
*	insert one control change for each ctrlflag[i] that is true
****************************************************************************/

private boolean ins_ctrl(score)
    event_type *score;
{
    int i;
    event_type ctrl;

    for (i = 1; i < nctrl; i++) {
	if (ctrlflag[i]) {
	    ctrlflag[i] = false;
	    if ((ctrl = ctrlalloc()) == NULL) {
		return false;
	    }
	    ctrl->ntime = round(thetime);
	    ctrl->nvoice = ctrl_voice(i, voice-1);
	    ctrl->nline = lineno;
	    ctrl->next = NULL;
	    ctrl->u.ctrl.value = ctrlval[i];
	    ins_event(score, ctrl);
	    ctrl_count ++;
	}
    }
    return true;
}

/****************************************************************************
*				ins_event
* Inputs:
*	event_type *p: a linked list in which to insert
*	event_type event: the new event to insert
* Effect: 
*	inserts event into score in reverse time order (this makes inserts
*	that are sequential in time go fast)
****************************************************************************/

private void ins_event(p, event)
    event_type *p;	/* the score */
    event_type event;	/* the new event to insert */
{
    event_type ptr = *p;
    event_type prv;

    if (ptr == NULL || event->ntime >= ptr->ntime) {
	event->next = ptr;	/* insert at head of list */
	*p = event;
    } else { /* list insert */
	while (ptr != NULL && event->ntime < ptr->ntime) {
	    prv = ptr;
	    ptr = ptr->next;
	}
	prv->next = event;
	event->next = ptr;
    }
}

/****************************************************************************
*				ins_note
* Inputs:
*	event_type *score: a linked list in which to insert
* Outputs:
*	returns true on success, false on error (not enough space)
* Effect:
*	note event (if any) corresponding to current line are inserted in 
*	score
* Implementation:
*	if a note on should occur after a note off and doesn't, and the
*	two notes have the same pitch, then the note off can cancel the
*	note on:
*		|------------------| <- this cancels *
*			   this -> |-----------| 
*	To make it unlikely that roundoff will cause this situation,
*	dur is decreased by one half of a clock tick before rounding.
*	Also, phase2 gives precedence to note-offs that are simultaneous
*	with note-ons.
****************************************************************************/

private boolean ins_note(score)
    event_type *score;
{
    event_type nalloc(), note;
    if ((note = nalloc()) == NULL) {
	return false;
    }
    note->ntime = round(thetime);
    note->nvoice = voice - 1;
    note->nline = lineno;
    note->next = NULL;
    if (rest_flag) note->u.note.npitch = NO_PITCH;	/* a rest */
    else note->u.note.npitch = pitch;
    note->u.note.ndur = round(dur - (unity/2));
    note->u.note.nloud = loud;
    note->u.note.nprogram = program;
    if (debug)
	printf("note: time %ld, dur %ld, pitch %d, voice %d, loudness %d\n",
		note->ntime, note->u.note.ndur, note->u.note.npitch,
		note->nvoice, note->u.note.nloud);
    ins_event(score, note);
    return true;
}

/****************************************************************************
*				issymbol
* Outputs: returns symbol number, or -1 if no match
* Assumes: token[1] has the symbol to look up (token[0] == '!')
****************************************************************************/

private int issymbol()
{
    int i, symb_num;
    char *sym;

    for (symb_num = 0; symb_num < sym_n; symb_num++) {
	sym = ssymbols[symb_num];
	i = 1;
	while (true) {
	    if (token[i] != *sym) break;
	    if (*sym == 0) return symb_num;
	    sym++; i++;
	}
    }
    return -1;
}

/****************************************************************************
*				marker
* Inputs:
*	int count: the number of characters to indent
* Effect: 
*	prints a carat (^) at the position specified on file stderr
****************************************************************************/

private void marker(count)
int count;
{
    int i;
    for (i=1; i<=count-1; i++)
	fprintf (stderr, " ");
    fprintf (stderr, "^\n");
}

/****************************************************************************
*				nalloc
* Outputs: returns event_type for an event allocated from heap, NULL on error
* Effect: allocates memory, decreases space accordingly
****************************************************************************/

private event_type nalloc()
{	
    char *malloc();
    event_type result;
    space -= sizeof(struct event_struct);
    if (space > 0) {
	result = ((event_type ) malloc (sizeof(struct event_struct)));
	if (result == NULL)
	    printf("Internal error: Out of memory, space = %ld.\n",space);
    } else result = NULL;
    return result;
}

/*****************************************************************
*			parseend
* Effect:
*	parse the note terminator, either ",", ";", or "\n"
*
****************************************************************/

private void parseend()
{
    linex += scan1(&line[linex]);
    switch (token[0]) {
	case ',':
	    ndurp = true;	/* switch that next time was specified */
	    ntime = 0;
	    break;
	case ';':
	case '\n':
	    break;
	default:
	    fferror("Internal error: illegal separator?");
	    break;
    }
}

/****************************************************************************
*				parsefield
* Effect: looks at first character of token and calls a parsing routine
*
****************************************************************************/

private void parsefield()
{
    fieldx = 1;
    switch (token[0]) {
	case 'T' : dotime(); break;
	case 'W': 
	case 'H':
	case 'Q':
	case 'S':
	case 'I': dodur()  ; break;
	case 'R': do_a_rest(); break;
	case 'A':
	case 'B':
	case 'C':
	case 'D':
	case 'E':
	case 'F':
	case 'G': dopitch(); break;
	case 'P': doabspitch (); break;
	case 'U': doabsdur(); break;
	case 'L': doloud(); break;
	case 'N': donextdur(); break;
	case 'J': doctrl(1); break;
	case 'K': doctrl(2); break;
	case 'M': doctrl(3); break;
	case 'O': doctrl(4); break;
	case 'X': doctrl(5); break;
	case 'Y': doctrl(6); break;
	case 'V': dovoice(); break;
	case 'Z': doprogram(); break;
	default : doerror(); break;
    }
}

/****************************************************************************
*				parsenote
* Inputs:
*	event_type *scoreptr: pointer to the note list
* Effect: 
*	parses a note line -- control events (if any) and note event (if
*	present) are inserted into *scoreptr
* Assumes:
*	line contains a string to be parsed
****************************************************************************/

private boolean parsenote(scoreptr)
    event_type *scoreptr;	/* the translated note list */
{
    boolean out_of_memory = false;

    ndurp = false;
    rest_flag = false;

    /* this loop reads tokens for a note */
    while (strlen(token) > 0) {
	parsefield();
	linex += scan(&line[linex]);
    }

    parseend(); /* take care of note terminator */

    /* insert a note if
     *	(1) a pitch was specified OR
     *	(2) no control was specified and this is not a rest 
     *		(it's a pitch by default) OR
     *	(3) there is a program change (even if this is a rest)
     *
     * NOTE: program changes during rests are advised since
     *	synthesizers may not be able to process a program
     *	change followed immediately by a note-on.  In fact, this
     *	is why we insert notes whose pitch is NO_PITCH -- so that
     *	the program change can be processed during the rest.
     */
    if (pitch_flag ||
	(!ctrlflag[0] && !rest_flag) ||
	(program != last_prog)) {
	out_of_memory = !ins_note(scoreptr);
	note_count ++;
    }
    /*
     * insert ctrl's last so that when the score is reversed,
     * they will be first.
     */
    if (ctrlflag[0]) {
	out_of_memory |= !ins_ctrl(scoreptr);
	ctrlflag[0] = false;
    }
    last_prog = program;

    if (ndurp) thetime += ntime;
    else thetime += dur;

    return out_of_memory;
}

/****************************************************************************
*				phase1
* Inputs:
*	FILE *fp: input file
* Outputs:
*	returns event_type: the parsed score
* Effect: 
*	parses score from input file and builds score data structure
****************************************************************************/

event_type phase1(fp)
FILE *fp;
{
    event_type score = NULL;	/* the translated note list */
    boolean out_of_memory = false;	/* set when no more memory */

    if (!init()) {  /* something bad happened in init(), STOP */
	fprintf(stderr,"WOOPS; something strange happened in INIT()!  ...exiting\n");
	exit(1);
	return NULL;	/* make lint happy */
    }

    lineno = 0;

    /* this loop reads lines */
    while ((fgets(line, linesize, fp) != NULL) && !out_of_memory) {
	lineno++;
	linex = 0;
	/* this loop reads notes from a line */
	while ((line[linex] != 0) && !out_of_memory) {
	    /* loop invariant: line[linex] is first char of next note */
	    pitch_flag = false;
	    linex += scan(&line[linex]);
	    if (!nullstring(token)) {
		if (token[0] == '*') docomment();
		else if (token[0] == '!') dospecial();
		else out_of_memory = parsenote(&score);
	    } else parseend();
	}
    }

    fprintf (stderr,"\n");

    if (out_of_memory) {
	fprintf(stderr,"Out of note memory at line %d,\n", lineno-1);
	fprintf(stderr,"    the rest of your file will be ignored.\n");
    }

    printf (
	"\nPhase 1 completed; %d note(s), %d ctrl(s) have been translated.\n",
	note_count, ctrl_count);

    reverse(&score);
    return score;
}

/****************************************************************************
*				reverse
* Inputs:
*	event_type *p: pointer to a list of notes and control events
* Effect: reverses note and control events in p
****************************************************************************/

private void reverse(p)
event_type *p;
{
    event_type p1, p2, p3;
    p1 = *p;
    if (p1 == NULL) return;
    p2 = p1->next;
    p1->next = NULL;
    while (p2 != NULL) {
	p3 = p2->next;
	p2->next = p1;
	p1 = p2;
	p2 = p3;
    }
    *p = p1;
}

/****************************************************************************
*				scan
* Inputs:
*	char *start: the string to scan
* Outputs:
*	returns int: the index of the next char in start to scan
* Effect: 
*	skips over leading blanks
*	copies characters from start into token, converting to upper case
*	scanning stops on delimiter: one of space, tab, newline, semicolon
****************************************************************************/

private int scan(start)
char *start;
{
    int i = 0;
    int j = 0;
    char c;

    while ((start[i] == ' ')||(start[i] == '\t')) i++;

    while ((c = start[i]) != ' ' && c != '\t' && c != '\n' &&
	   c != ',' && c != ';') {
	if (islower(start[i])) token[j] = toupper(start[i]);
	else token[j] = start[i];
	j++; i++;
    }
    token[j] = '\0';
    return i;
}

/****************************************************************************
*				scan1
* Inputs:
*	char *start: the string to scan
* Outputs:
*	returns int: the index of the next char in start to scan
* Effect: 
*	copies one char from start into token, converting to upper case
****************************************************************************/

private int scan1(start)
char *start;
{
    int i = 0;

    token[0] = *start;
    if (islower(token[0])) token[0] = toupper(token[0]);
    
    if (!nullstring(token)) {
	token[1] = '\0';
	i = 1;
    }
    return i;
}

/****************************************************************************
*				scanint
* Outputs:
*	returns int: the scanned integer
* Effect:
*	scans an unsigned integer from token, starting at fieldx
*	fieldx is incremented to end of the integer
****************************************************************************/

private int scanint()
{
    int i = 0;
    char c;
    while (fieldx < strlen(token)) {
	c = token[fieldx];
	if ((c >= '0') && (c<= '9')) {
	    i = (i*10) + (c - '0');
	    fieldx++;
	} else return i;
    }
    return i;
}
