%{

 /*
    csound_prs.lex:

    Copyright (C) 2011, 2016
    John ffitch

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
    02110-1301 USA
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "csoundCore.h"
#include "corfile.h"
#define YY_DECL int yylex (CSOUND *csound, yyscan_t yyscanner)
static void comment(yyscan_t);
static void do_comment(yyscan_t);
static void do_include(CSOUND *, int, yyscan_t);
static void do_new_include(CSOUND *, yyscan_t);
extern int isDir(char *);
static void do_macro_arg(CSOUND *, char *, yyscan_t);
static void do_macro(CSOUND *, char *, yyscan_t);
static void do_umacro(CSOUND *, char *, yyscan_t);
static void do_ifdef(CSOUND *, char *, yyscan_t);
static void do_ifdef_skip_code(CSOUND *, yyscan_t);
//static void print_csound_prsdata(CSOUND *,char *,yyscan_t);
static void csound_prs_line(CORFIL*, yyscan_t);
static void delete_macros(CSOUND*, yyscan_t);
#define MACDEBUG 1

#define S_INC (10)
static inline int isNameChar(int cc, int pos)
{
    unsigned char c = ((unsigned char) cc);
    return (isalpha(c) || (pos && (c == '_' || isdigit(c))));
}

#include "score_param.h"

//static void trace_alt_stack(CSOUND*, PRS_PARM*, int);

#define YY_EXTRA_TYPE  PRS_PARM *
#define PARM    yyget_extra(yyscanner)

#define YY_USER_INIT {csound_prs_scan_string(csound->scorestr->body, yyscanner); \
    csound_prsset_lineno(csound->scoLineOffset, yyscanner);             \
    /*yyg->yy_flex_debug_r=1;*/ PARM->macro_stack_size = 0;             \
    PARM->alt_stack = NULL; PARM->macro_stack_ptr = 0;                  \
    PARM->path = ".";                                                   \
    PARM->cf = csound->expanded_sco;                                    \
  }
static MACRO *find_definition(MACRO *, char *);
%}
%option reentrant
%option noyywrap
%option prefix="csound_prs"
%option outfile="Engine/csound_prslex.c"
%option stdout

NEWLINE         (\n|\r\n?)
STSTR           \"
ESCAPE          \\.
IDENT           [a-zA-Z_][a-zA-Z0-9_]*
MACRONAME       "$"`?[a-zA-Z_][a-zA-Z0-9_`]*
MACRONAMED      "$"`?[a-zA-Z_][a-zA-Z0-9_`]*\.
MACRONAMEA      "$"`?[a-zA-Z_][a-zA-Z0-9_`]*\(
MACRONAMEDA     "$"`?[a-zA-Z_][a-zA-Z0-9_`]*\.\(
MACROB          [a-zA-Z_][a-zA-Z0-9_]*\(
MACRO           [a-zA-Z_][a-zA-Z0-9_]*
NUMBER [0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?|\.[0-9]+([eE][-+]?[0-9]+)?|0[xX][0-9a-fA-F]+

STCOM           \/\*
INCLUDE         "#include"
INCLUDESTR      "#includestr"
DEFINE          #[ \t]*define
UNDEF           "#undef"
IFDEF           #ifn?def
ELSE            #else[ \t]*(;.*)?$
END             #end(if)?[ \t]*(;.*)?(\n|\r\n?)
LOOP            #loop
EXIT            #exit
CONT            \\[ \t]*(;.*)?(\n|\r\n?)
SEND            ^[ \t]*[es]
ROP             ^[ \t]*r

NP              np[0-9]+
NM              [nm][ \t]+

%X incl
%x macro
%x umacro
%x ifdef
%x lname

%%

{CONT}          {
#ifdef SCORE_PARSER
                  char bb[80];
#endif
                  csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),
                                       yyscanner);
#ifdef SCORE_PARSER
                  if (PARM->isString==0) {
                    sprintf(bb, "#sline %d ", csound_prsget_lineno(yyscanner));
                    corfile_puts(csound, bb, PARM->cf);
                  }
#endif
                }
{NEWLINE}       {
                  corfile_putc(csound, '\n', PARM->cf);
                  csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),
                                       yyscanner);
                  csound_prs_line(PARM->cf, yyscanner);
                }
"//"            {
                  if (PARM->isString != 1) {
                    comment(yyscanner);
                    corfile_putc(csound, '\n', PARM->cf);
                    csound_prs_line(PARM->cf, yyscanner);
                  }
                  else {
                    corfile_puts(csound, yytext, PARM->cf);
                  }
                }
";"             {
                  if (PARM->isString != 1) {
                    comment(yyscanner);
                    corfile_putc(csound, '\n', PARM->cf);
                    csound_prs_line(PARM->cf, yyscanner);
                  }
                  else {
                    corfile_puts(csound, yytext, PARM->cf);
                  }
                  //corfile_putline(csound_prsget_lineno(yyscanner),
                  //                PARM->cf);
                }
{STCOM}         {
                  if (PARM->isString != 1)
                    do_comment(yyscanner);
                  else
                    corfile_puts(csound, yytext, PARM->cf);
                }
{ESCAPE}        { corfile_puts(csound, yytext, PARM->cf); }
{STSTR}         {
                  corfile_putc(csound, '"', PARM->cf);
                  //printf("string start/end: >>%s<<\n", PARM->cf->body);
                  PARM->isString = !PARM->isString;
                  if (PARM->isinclude && PARM->isString==0) {
                    do_new_include(csound, yyscanner);
                    PARM->isinclude = 0;
                  }
                }
{MACRONAME}|{MACRONAMED}     {
                   MACRO     *mm = PARM->macros;
                   mm = find_definition(mm, yytext+1);
                   if (UNLIKELY(mm == NULL)) {
                     csound->Message(csound,Str("Undefined macro: '%s'"), yytext);
                     //csound->LongJmp(csound, 1);
                     corfile_puts(csound, "$error", PARM->cf);
                   }
                   else {
                     /* Need to read from macro definition */
                     if (mm->acnt<0) { /* Macro inhibitted */
                       corfile_puts(csound, yytext, PARM->cf);
                     }
                     else {
                       if (UNLIKELY(PARM->macro_stack_ptr+1 >=
                                    PARM->macro_stack_size)) {
                         //trace_alt_stack(csound, PARM, __LINE__);
                         //printf("***extending macro stack %p\n", PARM->alt_stack);
                         PARM->alt_stack =
                           (MACRON*)
                           csound->ReAlloc(csound, PARM->alt_stack,
                                   sizeof(MACRON)*(PARM->macro_stack_size+=S_INC));
                         if (UNLIKELY(PARM->alt_stack == NULL)) {
                           csound->Message(csound, Str("Memory exhausted"));
                           csound->LongJmp(csound, 1);
                         }
                         /* csound->DebugMsg(csound, "alt_stack now %d long\n", */
                         /*                  PARM->macro_stack_size); */
                       }
                       PARM->alt_stack[PARM->macro_stack_ptr].n = 0;
                       PARM->alt_stack[PARM->macro_stack_ptr].line =
                         csound_prsget_lineno(yyscanner);
                       PARM->alt_stack[PARM->macro_stack_ptr].path = NULL;
                       PARM->alt_stack[PARM->macro_stack_ptr++].s = NULL;
                       yypush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
                       csound_prsset_lineno(1, yyscanner);
                       if (UNLIKELY(PARM->depth>1022)) {
                         csound->Message(csound,
                                         Str("macros/include nested too deep: "));
                         csound->LongJmp(csound, 1);
                       }
                       PARM->lstack[++PARM->depth] =
                         (strchr(mm->body,'\n') ?file_to_int(csound, yytext) : 63);
                       yy_scan_string(mm->body, yyscanner);
                       /* csound->DebugMsg(csound,"%p\n", YY_CURRENT_BUFFER); */
                   }
                   }
                }
{MACRONAMEA}|{MACRONAMEDA}    {
                   MACRO     *mm = PARM->macros;
                   int err =0;
                   char      *mname;
                   int c, i, j, cnt=0;
                   //csound->DebugMsg(csound,"Macro with arguments call %s\n",
                   //                 yytext);
                   yytext[yyleng-1] = '\0';
                   mm = find_definition(PARM->macros, yytext+1);
                   if (UNLIKELY(mm == NULL)) {
                     csound->Message(csound,Str("Undefined macro: '%s'"), yytext);
                     corfile_puts(csound, "$error", PARM->cf);
                     //csound->LongJmp(csound, 1);
                   }
                   else {
                     mname = yytext;
                     /* Need to read from macro definition */
                     //csound->DebugMsg(csound,"Looking for %d args\n", mm->acnt);
                     for (j = 0; j < mm->acnt; j++) {
                       char  term = (j == mm->acnt - 1 ? ')' : '\'');
                       /* Compatability */
                       char  trm1 = (j == mm->acnt - 1 ? ')' : '#');
                       MACRO *nn = (MACRO*) csound->Malloc(csound, sizeof(MACRO));
                       //int   size = 100;
                       if (UNLIKELY(nn == NULL)) {
                         csound->Message(csound, Str("Memory exhausted"));
                         csound->LongJmp(csound, 1);
                       }
                       nn->name = csound->Malloc(csound, strlen(mm->arg[j]) + 1);
                       if (UNLIKELY(nn->name == NULL)) {
                         csound->Message(csound, Str("Memory exhausted"));
                         csound->LongJmp(csound, 1);
                       }
                       //csound->DebugMsg(csound,"Arg %d: %s\n", j+1, mm->arg[j]);
                       strcpy(nn->name, mm->arg[j]);
                       csound->Message(csound, "defining argument %s ",
                                        nn->name);
                       i = 0;
                       nn->body = (char*) csound->Malloc(csound, 100);
                       if (UNLIKELY(nn->body == NULL)) {
                         csound->Message(csound, Str("Memory exhausted"));
                         csound->LongJmp(csound, 1);
                       }
                       while (1) {
                         c = input(yyscanner);
                         if (cnt==0 && ( c==term || c==trm1)) break;
                         if (UNLIKELY(cnt==0 && c == ')')) {
                           csound->Message(csound,
                                           Str("Too few arguments to macro\n"));
                           corfile_puts(csound, "$error", PARM->cf);
                           err = 1; break;
                         }
                         if (c=='(') cnt++;
                         if (c==')') cnt--;
                         if (c == '\\') {
                           int newc = input(yyscanner);
                           if (newc != ')') nn ->body[i++] = c;
                           c = newc;
                         }
                         if (UNLIKELY(i > 98)) {
                           csound->Message(csound,
                                           Str("Missing argument terminator\n%.98s"),
                                           nn->body);
                           corfile_puts(csound, "$error", PARM->cf);
                           err = 1; break;
                         }
                         nn->body[i++] = c;
                         /* if (UNLIKELY(i >= size)) { */
                         /*   nn->body = csound->ReAlloc(csound, nn->body, */
                         /*   size += 100); */
                         /*   if (UNLIKELY(nn->body == NULL)) { */
                         /*     csound->Message(csound, Str("Memory exhausted")); */
                         /*     csound->LongJmp(csound, 1); */
                         /*   } */
                         /* } */
                       }
                       nn->body[i] = '\0';
                       csound->Message(csound, "as...#%s#\n", nn->body);
                       nn->acnt = 0;       /* No arguments for arguments */
                       nn->next = PARM->macros;
                       PARM->macros = nn;
                     }
                     if (!err) {
                       //csound->DebugMsg(csound,"New body: ...#%s#\n", mm->body);
                       if (UNLIKELY(PARM->macro_stack_ptr +1 >=
                                    PARM->macro_stack_size )) {
                         //trace_alt_stack(csound, PARM, __LINE__);
                         PARM->alt_stack =
                           (MACRON*)
                           csound->ReAlloc(csound, PARM->alt_stack,
                                   sizeof(MACRON)*(PARM->macro_stack_size+=S_INC));
                         if (UNLIKELY(PARM->alt_stack == NULL)) {
                           csound->Message(csound, Str("Memory exhausted"));
                           csound->LongJmp(csound, 1);
                         }
                         /* csound->DebugMsg(csound, */
                         /*        "macro_stack extends alt_stack to %d long\n", */
                         /*                  PARM->macro_stack_size); */
                       }
                       PARM->alt_stack[PARM->macro_stack_ptr].n =
                         PARM->macros->acnt;
                       PARM->alt_stack[PARM->macro_stack_ptr].line =
                         csound_prsget_lineno(yyscanner);
                       PARM->alt_stack[PARM->macro_stack_ptr].path = NULL;
                       PARM->alt_stack[PARM->macro_stack_ptr++].s = PARM->macros;
                       PARM->alt_stack[PARM->macro_stack_ptr].n = 0;
                       PARM->alt_stack[PARM->macro_stack_ptr].line =
                         csound_prsget_lineno(yyscanner);
                       PARM->alt_stack[PARM->macro_stack_ptr].path = NULL;
                       /* printf("stacked line = %llu at %d\n", */
                       /*  csound_prsget_lineno(yyscanner), */
                       /*                       PARM->macro_stack_ptr-1); */
                       PARM->alt_stack[PARM->macro_stack_ptr].s = NULL;
                       //csound->DebugMsg(csound,"Push %p macro stack\n",
                       //                 PARM->macros);
                       yypush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
                       csound_prsset_lineno(1, yyscanner);
                       if (UNLIKELY(PARM->depth>1022)) {
                         csound->Message(csound,
                                         Str("macros/include nested too deep: "));
                         //csound->LongJmp(csound, 1);
                         corfile_puts(csound, "$error", PARM->cf);
                         err = 1;
                       }
                     }
                     if (!err) {
                       PARM->lstack[++PARM->depth] =
                         (strchr(mm->body,'\n') ?file_to_int(csound, mname) : 63);
                       yy_scan_string(mm->body, yyscanner);
                     }
                   }
                 }
{INCLUDESTR}    {
                  if (PARM->isString != 1)
                    PARM->isinclude = 1;
                  else
                    corfile_puts(csound, yytext, csound->expanded_orc);
                }
{INCLUDE}       {
                  if (PARM->isString != 1)
                    BEGIN(incl);
                  else
                    corfile_puts(csound, yytext, PARM->cf);
                }
<incl>[ \t]*     /* eat the whitespace */
<incl>.         { /* got the include file name */
                  do_include(csound, yytext[0], yyscanner);
                  BEGIN(INITIAL);
                }
{NP}            { // special case for np3 type carries to avoid treatment as n
                 corfile_puts(csound, yytext, PARM->cf);
                }
{EXIT}          {
                 //printf("exit found: >>>%s<<<\n", PARM->cf->body);
                  corfile_putc(csound, '\0', PARM->cf);
                  corfile_putc(csound, '\0', PARM->cf);
                  delete_macros(csound, yyscanner);
                  return 0;
                }
<<EOF>>         {
                  MACRO *x, *y=NULL;
                  int n;
                  csound->DebugMsg(csound,"*********Leaving buffer %p\n",
                                   YY_CURRENT_BUFFER);
                  yypop_buffer_state(yyscanner);
                  PARM->depth--;
                  if (UNLIKELY(PARM->depth > 1024)) {
                    //csound->Die(csound, Str("unexpected EOF!!"));
                    csound->Message(csound, Str("unexpected EOF!!\n"));
                    csound->LongJmp(csound, 1);
                  }
                  PARM->llocn = PARM->locn; PARM->locn = make_location(PARM);
                  csound->DebugMsg(csound,"csound-prs(%d): loc=%u; lastloc=%u\n",
                                   __LINE__, PARM->llocn, PARM->locn);
                  if ( !YY_CURRENT_BUFFER ) yyterminate();
                  csound->DebugMsg(csound,"End of input; popping to %p\n",
                          YY_CURRENT_BUFFER);
                  csound_prs_line(PARM->cf, yyscanner);
                  n = PARM->alt_stack[--PARM->macro_stack_ptr].n;
                  if (PARM->alt_stack[PARM->macro_stack_ptr].path) {
                    //printf("restoring path from %s to %s\n",
                    //    PARM->path, PARM->alt_stack[PARM->macro_stack_ptr].path);
                    free(PARM->path);
                    PARM->path = PARM->alt_stack[PARM->macro_stack_ptr].path;
                  }
                  /* printf("lineno on stack is %llu\n", */
                  /*        PARM->alt_stack[PARM->macro_stack_ptr].line); */
                  csound->DebugMsg(csound,"n=%d\n", n);
                  if (n!=0) {
                    /* We need to delete n macros starting with y */
                    y = PARM->alt_stack[PARM->macro_stack_ptr].s;
                    x = PARM->macros;
                    if (x==y) {
                      while (n>0) {
                        mfree(csound, y->name); x=y->next;
                        mfree(csound, y); y=x; n--;
                      }
                      PARM->macros = x;
                    }
                    else {
                      MACRO *nxt = y->next;
                      while (x->next != y) x = x->next;
                      while (n>0) {
                        nxt = y->next;
                        mfree(csound, y->name); mfree(csound, y); y=nxt; n--;
                      }
                      x->next = nxt;
                    }
                    y->next = x;
                  }
                  csound_prsset_lineno(PARM->alt_stack[PARM->macro_stack_ptr].line,
                                       yyscanner);
                  csound->DebugMsg(csound, "csound_prs(%d): line now %d at %d\n",
                                   __LINE__,
                                   csound_prsget_lineno(yyscanner),
                                   PARM->macro_stack_ptr);
                  csound->DebugMsg(csound,
                                   "End of input segment: macro pop %p -> %p\n",
                                   y, PARM->macros);
                  csound_prsset_lineno(PARM->alt_stack[PARM->macro_stack_ptr].line,
                                       yyscanner);
                  //print_csound_prsdata(csound,"Before prs_line", yyscanner);
                  csound_prs_line(PARM->cf, yyscanner);
                  //print_csound_prsdata(csound,"After prs_line", yyscanner);
                }
{DEFINE}        {
                  if (PARM->isString != 1)
                    BEGIN(macro);
                  else
                    corfile_puts(csound, yytext, PARM->cf);
                }
<macro>[ \t]*    /* eat the whitespace */
<macro>{MACROB} {
                  yytext[yyleng-1] = '\0';
                  csound->DebugMsg(csound,"Define macro with args %s\n",
                                      yytext);
                  /* print_csound_prsdata(csound, "Before do_macro_arg",
                                          yyscanner); */
                  do_macro_arg(csound, yytext, yyscanner);
                  //print_csound_prsdata(csound,"After do_macro_arg", yyscanner);
                  BEGIN(INITIAL);
                }
<macro>{MACRO} {
                  csound->DebugMsg(csound,"Define macro %s\n", yytext);
                  /* print_csound_prsdata(csound,"Before do_macro", yyscanner); */
                  do_macro(csound, yytext, yyscanner);
                  //print_csound_prsdata(csound,"After do_macro", yyscanner);
                  BEGIN(INITIAL);
                }
<macro>.        { csound->Message(csound,
                                  Str("Unexpected character %c(%.2x) line %d\n"),
                                  yytext[0], yytext[0],
                                  csound_prsget_lineno(yyscanner));
                  csound->LongJmp(csound, 1);
                }
{UNDEF}         {
                  if (PARM->isString != 1)
                    BEGIN(umacro);
                  else
                    corfile_puts(csound, yytext, PARM->cf);
                }
<umacro>[ \t]*    /* eat the whitespace */
<umacro>{MACRO}  {
                  csound->DebugMsg(csound,"Undefine macro %s\n", yytext);
                  do_umacro(csound, yytext, yyscanner);
                  BEGIN(INITIAL);
                }

{IFDEF}         {
                  if (PARM->isString != 1) {
                    PARM->isIfndef = (yytext[3] == 'n');  /* #ifdef or #ifndef */
                    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),
                                         yyscanner);
                    corfile_putc(csound, '\n', PARM->cf);
                    csound_prs_line(PARM->cf, yyscanner);
                    BEGIN(ifdef);
                  }
                  else {
                    corfile_puts(csound, yytext, PARM->cf);
                  }
                }
<ifdef>[ \t]*     /* eat the whitespace */
<ifdef>{IDENT}  {
                  do_ifdef(csound, yytext, yyscanner);
                  BEGIN(INITIAL);
                }
{ELSE}          {
                  if (PARM->isString != 1) {
                    if (UNLIKELY(PARM->ifdefStack == NULL)) {
                      csound->Message(csound, Str("#else without #if\n"));
                      csound->LongJmp(csound, 1);
                    }
                    else if (UNLIKELY(PARM->ifdefStack->isElse)) {
                      csound->Message(csound, Str("#else after #else\n"));
                      csound->LongJmp(csound, 1);
                    }
                    PARM->ifdefStack->isElse = 1;
                    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),
                                         yyscanner);
                    corfile_putc(csound, '\n', PARM->cf);
                    csound_prs_line(PARM->cf, yyscanner);
                    do_ifdef_skip_code(csound, yyscanner);
                  }
                  else {
                    corfile_puts(csound, yytext, PARM->cf);
                  }
                }
{END}           {
                  if (PARM->isString != 1) {
                    IFDEFSTACK *pp = PARM->ifdefStack;
                    if (UNLIKELY(pp == NULL)) {
                      csound->Message(csound, Str("Unmatched #end\n"));
                      csound->LongJmp(csound, 1);
                    }
                    PARM->ifdefStack = pp->prv;
                    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),
                                         yyscanner);
                    corfile_putc(csound, '\n', PARM->cf);
                    csound_prs_line(PARM->cf, yyscanner);
                    mfree(csound, pp);
                  }
                  else {
                    corfile_puts(csound, yytext, PARM->cf);
                  }
                }
{NM}            {
                  corfile_putc(csound, yytext[0], PARM->cf);
                  if (!PARM->isString) BEGIN(lname);
                }
<lname>[ \t]*     /* eat the whitespace */
<lname>{IDENT}   {
                  corfile_putc(csound, ' ', PARM->cf);
                  corfile_puts(csound, yytext, PARM->cf);
                  BEGIN(INITIAL);
                }

"{"             {
                  int c, i;
                  PARM->repeat_index++;
                  if (UNLIKELY(PARM->repeat_index >= RPTDEPTH))
                    csound->Die(csound, Str("Loops are nested too deeply"));
                  PARM->repeat_mm_n[PARM->repeat_index] =
                    (MACRO*)csound->Malloc(csound, sizeof(MACRO));
                  if (UNLIKELY(PARM->repeat_mm_n[PARM->repeat_index] == NULL)) {
                    csound->Message(csound, Str("Memory exhausted"));
                    csound->LongJmp(csound, 1);
                  }
                  PARM->repeat_cnt_n[PARM->repeat_index] = 0;
                  do {
                    c = input(yyscanner);
                  } while (isblank(c));
                  if (c=='$') { // macro to yield count
                    char buf[256];
                    int i=0;
                    MACRO* mm;
                    //printf("*** macro count\n");
                    while (!isblank(c=input(yyscanner)) && c!='.') {
                      buf[i++] = c;
                    }
                    if (c=='.') c = input(yyscanner);
                    buf[i] = '\0';
                    //printf("*** lookup macro %s\n", buf);
                    if ((mm = find_definition(PARM->macros, buf))==NULL) {
                      csound->Message(csound,Str("Undefined macro: '%s'"), yytext);
                     //csound->LongJmp(csound, 1);
                     corfile_puts(csound, "$error", PARM->cf);
                     PARM->repeat_cnt_n[PARM->repeat_index] = 0;
                    }
                   else
                    PARM->repeat_cnt_n[PARM->repeat_index] =
                      atoi(mm->body);
                  }
                  else {
                    while (isdigit(c)) {
                      PARM->repeat_cnt_n[PARM->repeat_index] =
                      10 * PARM->repeat_cnt_n[PARM->repeat_index] + c - '0';
                      c = input(yyscanner);
                    }
                  }
                  if (UNLIKELY(PARM->repeat_cnt_n[PARM->repeat_index] <= 0
                               || !isspace(c)))
                    csound->Die(csound, Str("{: invalid repeat count"));
                  if (PARM->repeat_index > 1) {
                    char st[41];
                    int j;
                    for (j = 0; j < PARM->repeat_index; j++) {
                      st[j] = ' ';
                      st[j+1] = '\0';
                    }
                    if (UNLIKELY(csound->oparms->odebug))
                      csound->Message(csound, Str("%s Nested LOOP=%d Level:%d\n"),
                                      st, PARM->repeat_cnt_n[PARM->repeat_index],
                              PARM->repeat_index);
                  }
                  else {
                    if (UNLIKELY(csound->oparms->odebug))
                      csound->Message(csound, Str("External LOOP=%d Level:%d\n"),
                              PARM->repeat_cnt_n[PARM->repeat_index],
                              PARM->repeat_index);
                  }
                  while (isblank(c)) {
                    c = input(yyscanner);
                  }
                  for (i = 0; isNameChar(c, i) && i < (NAMELEN-1); i++) {
                    PARM->repeat_name_n[PARM->repeat_index][i] = c;
                    c = input(yyscanner);
                  }
                  PARM->repeat_name_n[PARM->repeat_index][i] = '\0';
                  unput(c);
                  /* Define macro for counter */
                  PARM->repeat_mm_n[PARM->repeat_index]->name =
                    csound->Malloc(csound,
                               strlen(PARM->repeat_name_n[PARM->repeat_index])+1);
                  if (UNLIKELY(PARM->repeat_mm_n[PARM->repeat_index]->name==NULL)) {
                    csound->Message(csound, Str("Memory exhausted"));
                    csound->LongJmp(csound, 1);
                  }
                  strcpy(PARM->repeat_mm_n[PARM->repeat_index]->name,
                         PARM->repeat_name_n[PARM->repeat_index]);
                  PARM->repeat_mm_n[PARM->repeat_index]->acnt = -1;
                  PARM->repeat_mm_n[PARM->repeat_index]->body =
                    csound->Calloc(csound, 16); // ensure nulls
                  PARM->repeat_mm_n[PARM->repeat_index]->body[0] = '0';
                  PARM->repeat_indx[PARM->repeat_index] = 0;
                  csound->DebugMsg(csound,"csound_prs(%d): repeat %s zero %p\n",
                                   __LINE__,
                                   PARM->repeat_name_n[PARM->repeat_index],
                                   PARM->repeat_mm_n[PARM->repeat_index]->body);
                  PARM->repeat_mm_n[PARM->repeat_index]->next = PARM->macros;
                  PARM->macros = PARM->repeat_mm_n[PARM->repeat_index];
                  while (input(yyscanner)!='\n') {}
                  PARM->cf_stack[PARM->repeat_index] = PARM->cf;
                  PARM->cf = corfile_create_w(csound);
        }
"}"     {
          int temp;
          CORFIL *bdy = PARM->cf;
          if (UNLIKELY((temp=PARM->repeat_cnt_n[PARM->repeat_index])==0)) {
            csound->Message(csound, Str("unmatched } in score\n"));
            csound->LongJmp(csound, 1);
          }
          corfile_puts(csound, "#loop", PARM->cf);
          corfile_putc(csound, '\0', PARM->cf);
          corfile_putc(csound, '\0', PARM->cf);
          PARM->cf = PARM->cf_stack[PARM->repeat_index];
          //printf("****Repeat body\n>>>%s<<<\n", bdy->body);
          PARM->repeat_mm_n[PARM->repeat_index]->acnt = 0; /* uninhibit */
          if (UNLIKELY(PARM->macro_stack_ptr +1 >= PARM->macro_stack_size )) {
            //trace_alt_stack(csound, PARM, __LINE__);
            PARM->alt_stack =
              (MACRON*)csound->ReAlloc(csound, PARM->alt_stack,
                                       sizeof(MACRON)*(PARM->macro_stack_size+=S_INC));
            if (UNLIKELY(PARM->alt_stack == NULL)) {
              csound->Message(csound, Str("Memory exhausted"));
              csound->LongJmp(csound, 1);
            }
          }
          csound->DebugMsg(csound,"csound_ps(%d): stacking line %d at %d\n",
                           __LINE__,
                           csound_prsget_lineno(yyscanner),PARM->macro_stack_ptr);
          PARM->alt_stack[PARM->macro_stack_ptr].n = 0;
          PARM->alt_stack[PARM->macro_stack_ptr].line =
            csound_prsget_lineno(yyscanner);
          PARM->alt_stack[PARM->macro_stack_ptr++].s = NULL;
          PARM->depth++;
          csound_prspush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
          csound_prs_scan_string(bdy->body, yyscanner);
          PARM->cf_stack[PARM->repeat_index] = bdy;
       }

{LOOP} {
          //printf("#loop found\n");
          yypop_buffer_state(yyscanner);
          PARM->depth--;
          PARM->llocn = PARM->locn; PARM->locn = make_location(PARM);
          /* printf("Loop structures: count = %d, name = %s, indx = %d\n", */
          /*        PARM->repeat_cnt_n[PARM->repeat_index], */
          /*        PARM->repeat_name_n[PARM->repeat_index], */
          /*        PARM->repeat_indx[PARM->repeat_index]); */
          if (++PARM->repeat_indx[PARM->repeat_index] !=
              PARM->repeat_cnt_n[PARM->repeat_index]) {
            snprintf(PARM->repeat_mm_n[PARM->repeat_index]->body, 16, "%d",
                     PARM->repeat_indx[PARM->repeat_index]);
            //printf(">>%s<<\n", PARM->cf_stack[PARM->repeat_index]->body);
            PARM->depth++;
            csound_prspush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
            csound_prs_scan_string(PARM->cf_stack[PARM->repeat_index]->body,
                                   yyscanner);
          }
          else {
            //printf("*** end loop\n");
            //printf(">>%s<<\n", PARM->cf->body);
            corfile_rm(csound, &PARM->cf_stack[PARM->repeat_index]);
            PARM->repeat_index--;
          }
       }
{ROP}  {
         if (PARM->isString) corfile_puts(csound, yytext, PARM->cf);
         else {
           int c, i;
           char buff[120];
           //printf("r detected %d\n",PARM->in_repeat_sect );
           if (PARM->in_repeat_sect==1) {
             char *tmp = strdup(yytext);
             // Previous r opcode not terminated so fake an s and redo
             //printf("unterminated r loop\n");
             for (i = yyleng-1; i >= 0; --i)
               unput(tmp[i]);
             unput('\n'); unput('s'); unput('\n');
             free(tmp);
           }
           else {
             PARM->repeat_sect_cnt = 0;
             PARM->in_repeat_sect = 1; /* Mark as recording */
             do {
               c = input(yyscanner);
             } while (isblank(c) || c=='\0');
             while (isdigit(c)) {
               PARM->repeat_sect_cnt =
                 10 * PARM->repeat_sect_cnt + c - '0';
               c = input(yyscanner);
             }
             if (UNLIKELY(PARM->repeat_sect_cnt <= 0
                          || !isspace(c))) {
               csound->Message(csound, Str("r: invalid repeat count %d"),
                               PARM->repeat_sect_cnt);
               csound->LongJmp(csound, 1);
             }
             if (UNLIKELY(csound->oparms->odebug))
               csound->Message(csound, Str("r LOOP=%d\n"), PARM->repeat_sect_cnt);
             while (isblank(c)) {
               c = input(yyscanner);
             }
             if (!isalpha(c)) { //no macro
               //printf("No macro\n");
               PARM->repeat_sect_mm =NULL;
             }
             else {
               for (i = 0; isNameChar(c, i) && i < (NAMELEN-1); i++) {
                 buff[i] = c;
                 c = input(yyscanner);
               }
               PARM->repeat_sect_mm =
                 (MACRO*)csound->Malloc(csound, sizeof(MACRO));
               if (UNLIKELY(PARM->repeat_sect_mm== NULL)) {
                 csound->Message(csound, Str("Memory exhausted"));
                 csound->LongJmp(csound, 1);
               }
               buff[i] = '\0';
               //printf("macro name %s\n", buff);
               /* Define macro for counter */
               PARM->repeat_sect_mm->name = cs_strdup(csound, buff);
               PARM->repeat_sect_mm->acnt = -1; /* inhibit */
               PARM->repeat_sect_mm->body = csound->Calloc(csound, 16);
               PARM->repeat_sect_mm->body[0] = '0';
               //csound->DebugMsg(csound,"repeat %s zero %s\n",
               //                 buff, PARM->repeat_sect_mm->body);
               PARM->repeat_sect_mm->next = PARM->macros; /* add to chain */
               PARM->macros = PARM->repeat_sect_mm;
             }
             unput(c);
             PARM->repeat_sect_line = PARM->line;
             PARM->repeat_sect_index = 0;
             //while (input(yyscanner)!='\n') {}
             PARM->repeat_sect_cf = PARM->cf;
             PARM->cf = corfile_create_w(csound);
           }
         }
        }
{SEND}  {
          if (!PARM->isString) {
            int op = yytext[strlen(yytext)-1];
            //printf("section end %d %c\n>>%s<<\n",
            //       PARM->in_repeat_sect, op, PARM->cf->body);
            if (PARM->in_repeat_sect==1) {
              corfile_putc(csound, 's', PARM->cf);
              while (1) {
                int c = input(yyscanner);
                if (c=='\n') break;
                if (!isspace(c)&&!isdigit(c)&&!strchr(".e+-",c)) { unput(c); break;}
                corfile_putc(csound, c, PARM->cf);
              }
              corfile_putc(csound, '\n', PARM->cf);
              unput(op);
              struct yyguts_t *yyg =(struct yyguts_t*)yyscanner;
              PARM->in_repeat_sect=2;
              //printf("****Repeat body\n>>>%s<<<\n", PARM->cf->body);
              if (PARM->repeat_sect_mm)
                PARM->repeat_sect_mm->acnt = 0; /* uninhibit */
              csound_prspush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
              csound_prs_scan_string(PARM->cf->body, yyscanner);
              { CORFIL *tmp = PARM->cf;
                PARM->cf = PARM->repeat_sect_cf;
                PARM->repeat_sect_cf = tmp;
              }
              PARM->line = PARM->repeat_sect_line;
            }
            else if (PARM->in_repeat_sect==2) {
              corfile_putc(csound, 's', PARM->cf);
              while (1) {
                int c = input(yyscanner);
                if (c=='\n') break;
                corfile_putc(csound, c, PARM->cf);
              }
              corfile_putc(csound, '\n', PARM->cf);
              yypop_buffer_state(yyscanner);
              PARM->llocn = PARM->locn; PARM->locn = make_location(PARM);
              //printf("repeat section %d %d\n",
              //       PARM->repeat_sect_index,PARM->repeat_sect_cnt);
              PARM->repeat_sect_index++;
              if (PARM->repeat_sect_index<PARM->repeat_sect_cnt) {
                if (PARM->repeat_sect_mm) {
                  snprintf(PARM->repeat_sect_mm->body, 16, "%d",
                           PARM->repeat_sect_index);
                  //printf("%s now %s\n",
                  //       PARM->repeat_sect_mm->name,PARM->repeat_sect_mm->body);
                }
                csound_prspush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
                csound_prs_scan_string(PARM->repeat_sect_cf->body, yyscanner);
                PARM->line = PARM->repeat_sect_line;
              }
              else {
                //corfile_puts(csound, 'r', PARM->cf);
                /* corfile_putc(csound, '\n', PARM->cf); */
                //printf("end of loop\n");
                PARM->in_repeat_sect=0;
                corfile_rm(csound, &PARM->repeat_sect_cf);
                //csound->Free(csound, PARM->repeat_sect_mm->body);
                //PARM->repeat_sect_mm->body = NULL;
              }
            }
            else {
              corfile_putc(csound, op, PARM->cf);
              while (1) {
                int c = input(yyscanner);
                //printf("**copy %.2x(%c)\n", c, c);
                corfile_putc(csound, c, PARM->cf);
                if (c=='\n') break;
                if (c=='\0') break;
              }
            }
          }
          else corfile_puts(csound, yytext, PARM->cf);
        }
.       { corfile_putc(csound, yytext[0], PARM->cf); }

%%
static void comment(yyscan_t yyscanner)              /* Skip until nextline */
{
    char c;
    struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
    while ((c = input(yyscanner)) != '\n' && c != '\r') { /* skip */
      if (UNLIKELY((int)c == EOF)) {
        YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
          YY_BUFFER_EOF_PENDING;
        return;
      }
    }
    if (c == '\r' && (c = input(yyscanner)) != '\n') {
      if (LIKELY((int)c != EOF))
        unput(c);
      else
        YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
          YY_BUFFER_EOF_PENDING;
    }
    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),yyscanner);
}

static void do_comment(yyscan_t yyscanner)         /* Skip until * and / chars */
{
    int c;
    struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
 TOP:
    c = input(yyscanner);
    switch (c) {
    NL:
    case '\n':
      csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),yyscanner);
      goto TOP;
    case '*':
    AST:
      c = input(yyscanner);
      switch (c) {
      case '*':
        goto AST;
      case '\n':
        goto NL;
      case '/':
        return;
      case EOF:
        goto ERR;
      default:
        goto TOP;
      }
    case EOF:
    ERR:
      YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
        YY_BUFFER_EOF_PENDING;
      return;
    default:
      goto TOP;
    }
}

static void do_include(CSOUND *csound, int term, yyscan_t yyscanner)
{
    char buffer[100];
    int p=0;
    int c;
    CORFIL *cf;
    struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
    while ((c=input(yyscanner))!=term) {
      if (c=='\n' || c==EOF) {
        csound->Warning(csound, Str("Ill formed #include ignored"));
        return;
      }
      buffer[p] = c;
      p++;
    }
    buffer[p] = '\0';
    while ((c=input(yyscanner))!='\n');
    if (UNLIKELY(PARM->depth++>=1024)) {
      csound->Die(csound, Str("Includes nested too deeply"));
    }
    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner), yyscanner);
    csound->DebugMsg(csound,"line %d at end of #include line\n",
                     csound_prsget_lineno(yyscanner));
    {
      uint8_t n = file_to_int(csound, buffer);
      //char bb[128];
      PARM->lstack[PARM->depth] = n;
      //sprintf(bb, "#source %llu\n", PARM->locn = make_location(PARM));
      PARM->llocn = PARM->locn;
#ifdef SCORE_PARSER
      //corfile_puts(csound, bb, PARM->cf);
#endif
    }
    csound->DebugMsg(csound,"reading included file \"%s\"\n", buffer);
    if (UNLIKELY(isDir(buffer)))
      csound->Warning(csound, Str("%s is a directory; not including"), buffer);
    if (PARM->path && buffer[0]!= '/') { // if nested included directories
      char tmp[1024];
      printf("using path %s\n", PARM->path);
      strncpy(tmp, PARM->path, 1023);
      strcat(tmp, "/");
      strncat(tmp, buffer, 1022-strlen(tmp));
      cf = copy_to_corefile(csound, tmp, "INCDIR", 0);
    }
    else cf = copy_to_corefile(csound, buffer, "INCDIR", 0);
    if (UNLIKELY(cf == NULL))
      csound->Die(csound,
                  Str("Cannot open #include'd file %s\n"), buffer);
    if (UNLIKELY(PARM->macro_stack_ptr +1 >= PARM->macro_stack_size )) {
      //trace_alt_stack(csound, PARM, __LINE__);
      PARM->alt_stack =
        (MACRON*) csound->ReAlloc(csound, PARM->alt_stack,
                                  sizeof(MACRON)*(PARM->macro_stack_size+=S_INC));
      if (UNLIKELY(PARM->alt_stack == NULL)) {
        csound->Message(csound, Str("Memory exhausted"));
        csound->LongJmp(csound, 1);
      }
      /* csound->DebugMsg(csound, "alt_stack now %d long,\n", */
      /*                  PARM->macro_stack_size); */
    }
    csound->DebugMsg(csound,"csound_prs(%d): stacking line %d at %d\n", __LINE__,
           csound_prsget_lineno(yyscanner),PARM->macro_stack_ptr);
    PARM->alt_stack[PARM->macro_stack_ptr].n = 0;
    PARM->alt_stack[PARM->macro_stack_ptr].line = csound_prsget_lineno(yyscanner);
    if (strrchr(buffer,'/')) {
      PARM->alt_stack[PARM->macro_stack_ptr].path = PARM->path;
      printf("setting path from %s to ", PARM->path);
      PARM->path = strdup(buffer); /* wasteful! */
      *(strrchr(PARM->path,'/')) = '\0';
      printf("%s\n",PARM->path);
    }
    else PARM->alt_stack[PARM->macro_stack_ptr].path = NULL;
    PARM->alt_stack[PARM->macro_stack_ptr++].s = NULL;
    csound_prspush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
    csound_prs_scan_string(cf->body, yyscanner);
    corfile_rm(csound, &cf);
    csound->DebugMsg(csound,"Set line number to 1\n");
    csound_prsset_lineno(1, yyscanner);
}

void  do_new_include(CSOUND *csound, yyscan_t yyscanner)
{
    char buffer[128];
    CORFIL *cf = PARM->cf;
    int p = cf->p-2;
    struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;

    //printf("*** in do_new_include\n");
    cf->body[p+1] = '\0';
    while (cf->body[p]!='"') p--;
    //printf("*** name is >>%s<<\n", &cf->body[p]);
    cf->body[p] = '\0';
    strncpy(buffer, &cf->body[p+1],127); buffer[127]='\0';
    cf->p = p;
    //printf("****buffer >>%s<<\n", buffer);
    while ((input(yyscanner))!='\n');
    if (UNLIKELY(PARM->depth++>=1024)) {
      csound->Die(csound, Str("Includes nested too deeply"));
    }
    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner), yyscanner);
    csound->DebugMsg(csound,"line %d at end of #include line\n",
                     csound_prsget_lineno(yyscanner));
    {
      uint8_t n = file_to_int(csound, buffer);
      //char bb[128];
      PARM->lstack[PARM->depth] = n;
      //sprintf(bb, "#source %"PRIu64"\n", PARM->locn = make_location(PARM));
      PARM->llocn = PARM->locn;
      //corfile_puts(csound, bb, PARM->cf);
    }
    csound->DebugMsg(csound,"reading included file \"%s\"\n", buffer);
    if (UNLIKELY(isDir(buffer)))
      csound->Warning(csound, Str("%s is a directory; not including"), buffer);
    if (PARM->path && buffer[0]!='/') {
      char tmp[1024];
      strncpy(tmp, PARM->path, 1023);
      strcat(tmp, "/");
      strncat(tmp, buffer, 1022-strlen(tmp));
      cf = copy_to_corefile(csound, tmp, "INCDIR", 0);
    }
    else cf = copy_to_corefile(csound, buffer, "INCDIR", 0);
    if (UNLIKELY(cf == NULL))
      csound->Die(csound,
                  Str("Cannot open #include'd file %s\n"), buffer);
    if (UNLIKELY(PARM->macro_stack_ptr +1 >= PARM->macro_stack_size )) {
      //trace_alt_stack(csound, PARM, __LINE__);
      PARM->alt_stack =
        (MACRON*) csound->ReAlloc(csound, PARM->alt_stack,
                                  sizeof(MACRON)*(PARM->macro_stack_size+=S_INC));
      if (UNLIKELY(PARM->alt_stack == NULL)) {
        csound->Message(csound, Str("Memory exhausted"));
        csound->LongJmp(csound, 1);
      }
      /* csound->DebugMsg(csound, "alt_stack now %d long,\n", */
      /*                  PARM->macro_stack_size); */
    }
    csound->DebugMsg(csound,"csound_prs(%d): stacking line %d at %d\n", __LINE__,
           csound_prsget_lineno(yyscanner),PARM->macro_stack_ptr);
    PARM->alt_stack[PARM->macro_stack_ptr].n = 0;
    PARM->alt_stack[PARM->macro_stack_ptr].line = csound_prsget_lineno(yyscanner);
    if (strrchr(buffer,'/')) {
      PARM->alt_stack[PARM->macro_stack_ptr].path = PARM->path;
      PARM->path = strdup(buffer); /* wasteful! */
      *(strrchr(PARM->path,'/')) = '\0';
    }
    else PARM->alt_stack[PARM->macro_stack_ptr].path = NULL;
    PARM->alt_stack[PARM->macro_stack_ptr++].s = NULL;
    csound_prspush_buffer_state(YY_CURRENT_BUFFER, yyscanner);
    csound_prs_scan_string(cf->body, yyscanner);
    corfile_rm(csound, &cf);
    csound->DebugMsg(csound,"Set line number to 1\n");
    csound_prsset_lineno(1, yyscanner);
}

static void do_macro_arg(CSOUND *csound, char *name0, yyscan_t yyscanner)
{
    MACRO *mm = (MACRO*) csound->Malloc(csound, sizeof(MACRO));
    int   arg = 0, i, c;
    int   size = 100;
    int mlen = 40;
    char *q = name0;
    char *mname = malloc(mlen);

    if (UNLIKELY(mm == NULL||mname == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }
    mm->margs = MARGS;    /* Initial size */
    mm->name = (char*)csound->Malloc(csound, strlen(name0) + 1);
    if (UNLIKELY(mm->name == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }
    strcpy(mm->name, name0);
    do {
      i = 0;
      q = name0;
      mname[i++] = '`';
      while ((c = *q++)) {
        mname[i++] = c;
        if (UNLIKELY(i==mlen))
          mname = (char *)realloc(mname, mlen+=40);
        if (UNLIKELY(mname == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
      }
      mname[i++] = '`';
      if (UNLIKELY(i==mlen)) {
          mname = (char *)realloc(mname, mlen+=40);
        if (UNLIKELY(mname == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
      }
      mname[i++] = '`';
      if (UNLIKELY(i==mlen)) {
        mname = (char *)realloc(mname, mlen+=40);
        if (UNLIKELY(mname == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
      }
      while (isspace((c = input(yyscanner))));

      while (isNameChar(c, i)) {
        mname[i++] = c;
        if (UNLIKELY(i==mlen)) {
          mname = (char *)realloc(mname, mlen+=40);
          if (UNLIKELY(mname == NULL)) {
            csound->Message(csound, Str("Memory exhausted"));
            csound->LongJmp(csound, 1);
          }
        }
        c = input(yyscanner);
      }
      mname[i] = '\0';
      mm->arg[arg] = csound->Malloc(csound, i + 1);
      if (UNLIKELY(mm->arg[arg] == NULL)) {
        csound->Message(csound, Str("Memory exhausted"));
        csound->LongJmp(csound, 1);
      }
      strcpy(mm->arg[arg++], mname);
      if (UNLIKELY(arg >= mm->margs)) {
        mm = (MACRO*) csound->ReAlloc(csound, mm, sizeof(MACRO)
                               + mm->margs * sizeof(char*));
        if (UNLIKELY(mm == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
        mm->margs += MARGS;
      }
      while (isspace(c))
        c = input(yyscanner);
    } while (c == '\'' || c == '#');
    if (UNLIKELY(c != ')')) {
      csound->Message(csound, Str("macro error\n"));
    }
    free(mname);
    c = input(yyscanner);
    while (c!='#') {
      if (UNLIKELY(c==EOF)) csound->Die(csound, Str("define macro runaway\n"));
      else if (c==';') {
        while ((c=input(yyscanner))!= '\n')
          if (UNLIKELY(c==EOF)) {
            csound->Die(csound, Str("define macro runaway\n"));
          }
      }
      else if (c=='/') {
        if ((c=input(yyscanner))=='/') {
          while ((c=input(yyscanner))!= '\n')
            if (UNLIKELY(c==EOF))
              csound->Die(csound, Str("define macro runaway\n"));
        }
        else if (c=='*') {
          while ((c=input(yyscanner))!='*') {
          again:
            if (UNLIKELY(c==EOF))
              csound->Die(csound, Str("define macro runaway\n"));
          }
          if ((c=input(yyscanner))!='/') goto again;
        }
      }
      else if (UNLIKELY(!isspace(c)))
        csound->Die(csound,
               Str("define macro unexpected character %c(0x%.2x) awaiting #\n"),
                    c, c);
      c = input(yyscanner); /* skip to start of body */
    }
    mm->acnt = arg;
    i = 0;
    mm->body = (char*) csound->Malloc(csound, 100);
    if (UNLIKELY(mm->body == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }

    while ((c = input(yyscanner)) != '#') { /* read body */
      if (UNLIKELY(c == EOF))
        csound->Die(csound, Str("define macro with args: unexpected EOF"));
      if (c=='$') {             /* munge macro name? */
        int n = strlen(name0)+4;
        if (UNLIKELY(i+n >= size)) {
          mm->body = csound->ReAlloc(csound, mm->body, size += 100);
          if (UNLIKELY(mm->body == NULL)) {
            csound->Message(csound, Str("Memory exhausted"));
            csound->LongJmp(csound, 1);
          }
        }
        mm->body[i] = '$'; mm->body[i+1] = '`';
        strcpy(&mm->body[i+2], name0);
        mm->body[i + n - 2] = '`'; mm->body[i + n - 1] = '`';
        i+=n;
        continue;
      }
      mm->body[i++] = c=='\r'?'\n':c;
      if (UNLIKELY(i >= size)) {
        mm->body = csound->ReAlloc(csound, mm->body, size += 100);
        if (UNLIKELY(mm->body == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
      }
      if (c == '\\') {                    /* allow escaped # */
        mm->body[i++] = c = input(yyscanner);
        if (UNLIKELY(i >= size)) {
          mm->body = csound->ReAlloc(csound, mm->body, size += 100);
          if (UNLIKELY(mm->body == NULL)) {
            csound->Message(csound, Str("Memory exhausted"));
            csound->LongJmp(csound, 1);
          }
        }
      }
      if (UNLIKELY(c == '\n' || c == '\r')) {
        csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),yyscanner);
        corfile_putc(csound, '\n', PARM->cf);
        csound_prs_line(PARM->cf, yyscanner);
      }
    }
    mm->body[i] = '\0';
    mm->next = PARM->macros;
    PARM->macros = mm;
}

static void do_macro(CSOUND *csound, char *name0, yyscan_t yyscanner)
{
    MACRO *mm = (MACRO*) csound->Malloc(csound, sizeof(MACRO));
    int   i, c;
    int   size = 100;
    if (UNLIKELY(mm == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }

    mm->margs = MARGS;    /* Initial size */
    csound->DebugMsg(csound,"Macro definition for %s\n", name0);
    mm->name = (char*)csound->Malloc(csound, strlen(name0) + 1);
    if (UNLIKELY(mm->name == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }
    strcpy(mm->name, name0);
    mm->acnt = 0;
    i = 0;
    while ((c = input(yyscanner)) != '#') {
      if (UNLIKELY(c==EOF)) csound->Die(csound, Str("define macro runaway\n"));
      else if (c==';') {
        while ((c=input(yyscanner))!= '\n')
          if (UNLIKELY(c==EOF)) {
            csound->Die(csound, Str("define macro runaway\n"));
          }
      }
      else if (c=='/') {
        if ((c=input(yyscanner))=='/') {
          while ((c=input(yyscanner))!= '\n')
            if (UNLIKELY(c==EOF))
              csound->Die(csound, Str("define macro runaway\n"));
        }
        else if (c=='*') {
          while ((c=input(yyscanner))!='*') {
          again:
            if (UNLIKELY(c==EOF))
              csound->Die(csound, Str("define macro runaway\n"));
          }
          if ((c=input(yyscanner))!='/') goto again;
        }
      }
      else if (UNLIKELY(!isspace(c)))
        csound->Die(csound,
                    Str("define macro unexpected character %c(0x%.2x)"
                        "awaiting #\n"),
                    c, c);
    }
    mm->body = (char*) csound->Malloc(csound, 100);
    if (UNLIKELY(mm->body == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }
    while ((c = input(yyscanner)) != '#') {
      if (UNLIKELY(c == EOF || c==0))
        csound->Die(csound, Str("define macro: unexpected EOF"));
      mm->body[i++] = c=='\r'?'\n':c;
      if (UNLIKELY(i >= size)) {
        mm->body = csound->ReAlloc(csound, mm->body, size += 100);
        if (UNLIKELY(mm->body == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
      }
      if (c == '\\') {                    /* allow escaped # */
        mm->body[i++] = c = input(yyscanner);
        if (UNLIKELY(i >= size)) {
          mm->body = csound->ReAlloc(csound, mm->body, size += 100);
          if (UNLIKELY(mm->body == NULL)) {
            csound->Message(csound, Str("Memory exhausted"));
            csound->LongJmp(csound, 1);
          }
        }
      }
      if (UNLIKELY(c == '\n' || c == '\r')) {
        csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),yyscanner);
        corfile_putc(csound, '\n', PARM->cf);
        csound_prs_line(PARM->cf, yyscanner);
      }
    }
    mm->body[i] = '\0';
    csound->DebugMsg(csound,"Body #%s#\n", mm->body);
    mm->next = PARM->macros;
    PARM->macros = mm;
}

static void do_umacro(CSOUND *csound, char *name0, yyscan_t yyscanner)
{
    int i,c;
    if (UNLIKELY(csound->oparms->msglevel))
      csound->Message(csound,Str("macro %s undefined\n"), name0);
    csound->DebugMsg(csound, "macro %s undefined\n", name0);
    if (strcmp(name0, PARM->macros->name)==0) {
      MACRO *mm=PARM->macros->next;
      mfree(csound, PARM->macros->name); mfree(csound, PARM->macros->body);
      for (i=0; i<PARM->macros->acnt; i++)
        mfree(csound, PARM->macros->arg[i]);
      mfree(csound, PARM->macros); PARM->macros = mm;
    }
    else {
      MACRO *mm = PARM->macros;
      MACRO *nn = mm->next;
      while (strcmp(name0, nn->name) != 0) {
        mm = nn; nn = nn->next;
        if (UNLIKELY(nn == NULL)) {
          csound->Message(csound, Str("Undefining undefined macro"));
          csound->LongJmp(csound, 1);
        }
      }
      mfree(csound, nn->name); mfree(csound, nn->body);
      for (i=0; i<nn->acnt; i++)
        mfree(csound, nn->arg[i]);
      mm->next = nn->next; mfree(csound, nn);
    }
    while ((c=input(yyscanner)) != '\n' &&
           c != EOF && c != '\r'); /* ignore rest of line */
    csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),yyscanner);
}

static void do_ifdef(CSOUND *csound, char *name0, yyscan_t yyscanner)
{
    int c;
    MACRO *mm;
    IFDEFSTACK *pp;
    pp = (IFDEFSTACK*) csound->Calloc(csound, sizeof(IFDEFSTACK));
    if (UNLIKELY(pp == NULL)) {
      csound->Message(csound, Str("Memory exhausted"));
      csound->LongJmp(csound, 1);
    }
    pp->prv = PARM->ifdefStack;
    pp->isDef = PARM->isIfndef;
    for (mm = PARM->macros; mm != NULL; mm = mm->next) {
      if (strcmp(name0, mm->name) == 0) {
        pp->isDef ^= (unsigned char) 1;
        break;
      }
    }
    PARM->ifdefStack = pp;
    pp->isSkip = pp->isDef ^ (unsigned char) 1;
    if (pp->isSkip)
      do_ifdef_skip_code(csound, yyscanner);
    else
      while ((c = input(yyscanner)) != '\n' && c != '\r' && c != EOF);
}

static void do_ifdef_skip_code(CSOUND *csound, yyscan_t yyscanner)
{
    int i, c, nested_ifdef = 0;
    char buf[8];
    IFDEFSTACK *pp;
    /* buf = (char*)malloc(8*sizeof(char)); */
    /* if (UNLIKELY(buf == NULL)) { */
    /*   csound->Message(csound, Str("Memory exhausted")); */
    /*   csound->LongJmp(csound, 1); */
    /* } */
    pp = PARM->ifdefStack;
    c = input(yyscanner);
    for (;;) {
      while (c!='\n' && c!= '\r') {
        if (UNLIKELY(c == EOF)) {
          csound->Message(csound, Str("Unmatched #if%sdef\n"),
                          PARM->isIfndef ? "n" : "");
          csound->LongJmp(csound, 1);
        }
        c = input(yyscanner);
    }
      csound_prsset_lineno(1+csound_prsget_lineno(yyscanner),
                           yyscanner);
      corfile_putc(csound, '\n', PARM->cf);
      csound_prs_line(PARM->cf, yyscanner);
      while (isblank(c = input(yyscanner)));  /* eat the whitespace */
      if (c == '#') {
        for (i=0; islower(c = input(yyscanner)) && i < 7; i++)
          buf[i] = c;
        buf[i] = '\0';
        if (strcmp("end", buf) == 0 || strcmp("endif", buf) == 0) {
          if (nested_ifdef-- == 0) {
            PARM->ifdefStack = pp->prv;
            mfree(csound, pp);
            break;
          }
        }
        else if (strcmp("ifdef", buf) == 0 || strcmp("ifndef", buf) == 0) {
          nested_ifdef++;
        }
        else if (strcmp("else", buf) == 0 && nested_ifdef == 0) {
          if (UNLIKELY(pp->isElse)) {
            csound->Message(csound, Str("#else after #else\n"));
            csound->LongJmp(csound, 1);
          }
          pp->isElse = 1;
          break;
        }
      }
    }
    //free(buf);
    while (c != '\n' && c != EOF && c != '\r') c = input(yyscanner);
}

static void delete_macros(CSOUND *csound, yyscan_t yyscanner)
{
    MACRO * qq = PARM->macros;
    if (qq) {
      MACRO *mm = qq;
      while (mm) {
        csound->Free(csound, mm->body);
        csound->Free(csound, mm->name);
        qq = mm->next;
        csound->Free(csound, mm);
        mm = qq;
       }
    }
}

void cs_init_smacros(CSOUND *csound, PRS_PARM *qq, NAMES *nn)
{
    while (nn) {
      char  *s = nn->mac;
      char  *p = strchr(s, '=');
      char  *mname;
      MACRO *mm;

      if (p == NULL)
        p = s + strlen(s);
      if (UNLIKELY(csound->oparms->msglevel & 7))
        csound->Message(csound, Str("Macro definition for %*s\n"), (int)(p - s), s);
      s = strchr(s, ':') + 1;                   /* skip arg bit */
      if (UNLIKELY(s == NULL || s >= p)) {
        csound->Die(csound, Str("Invalid macro name for --smacro"));
      }
      mname = (char*) csound->Malloc(csound, (p - s) + 1);
      if (UNLIKELY(mname == NULL)) {
        csound->Message(csound, Str("Memory exhausted"));
        csound->LongJmp(csound, 1);
      }
      strncpy(mname, s, p - s);
      mname[p - s] = '\0';
      /* check if macro is already defined */
      for (mm = qq->macros; mm != NULL; mm = mm->next) {
        if (strcmp(mm->name, mname) == 0)
          break;
      }
      if (mm == NULL) {
        mm = (MACRO*) csound->Calloc(csound, sizeof(MACRO));
        if (UNLIKELY(mm  == NULL)) {
          csound->Message(csound, Str("Memory exhausted"));
          csound->LongJmp(csound, 1);
        }
        mm->name = mname;
        mm->next = qq->macros;
        qq->macros = mm;
      }
      else
        mfree(csound, mname);
      mm->margs = MARGS;    /* Initial size */
      mm->acnt = 0;
      if (*p != '\0')
        p++;
      mm->body = (char*) csound->Malloc(csound, strlen(p) + 1);
      if (UNLIKELY(mm->body == NULL)) {
        csound->Message(csound, Str("Memory exhausted"));
        csound->LongJmp(csound, 1);
      }
      strcpy(mm->body, p);
      nn = nn->next;
    }
}

static void csound_prs_line(CORFIL* cf, void *yyscanner)
{
    int n = csound_prsget_lineno(yyscanner);
    //printf("line number %d\n", n);
    /* This assumes that the initial line was not written with this system  */
    if (cf->p>0 && cf->body[cf->p-1]=='\n') {
      uint64_t locn = PARM->locn;
#if 0
      uint64_t llocn = PARM->llocn;
      if (locn != llocn) {
        //char bb[80];
        //sprintf(bb, "#source %llu\n", locn);
        //corfile_puts(bb, cf);
      }
#endif
      PARM->llocn = locn;
#ifdef SCORE_PARSER
      //if (n!=PARM->line+1) {
      //char bb[80];
        //sprintf(bb, "#line   %d\n", n);
        //printf("#line %d\n", n);
        //corfile_puts(bb, cf);
      //}
#endif
    }
    PARM->line = n;
}

static MACRO *find_definition(MACRO *mmo, char *s)
{
    MACRO *mm = mmo;
    if (s[strlen(s)-1]=='.') s[strlen(s)-1]='\0';
    else if (s[strlen(s)-2]=='.' && s[strlen(s)-1]=='(') {
      s[strlen(s)-2] = '('; s[strlen(s)-1] = '\0'; }
    //printf("****Looking for %s\n", s);
    while (mm != NULL) {  /* Find the definition */
      //printf("looking at %p(%s) body #%s#\n", mm, mm->name, mm->body);
      if (!(strcmp(s, mm->name))) break;
      mm = mm->next;
    }
    if (mm == NULL) {
      mm = mmo;
      s++;                      /* skip _ */
    looking:
      while (*s++!='`') { if (*s=='\0') return NULL; }
      if (*s++!='`') { s--; goto looking; }
      //printf("now try looking for %s\n", s);
      while (mm != NULL) {  /* Find the definition */
        //printf("looking at %p(%s) body #%s#\n", mm, mm->name, mm->body);
        if (!(strcmp(s, mm->name))) break;
        mm = mm->next;
      }
    }
    //if (mm) printf("found body #%s#%c\n****\n", mm->body, mm->acnt?'X':' ');
    return mm;
}

/* static void trace_alt_stack(CSOUND* csound, PRS_PARM* p, int line) */
/* { */
/*  printf("***Line %d extend alt_stack from %d/%d (%p)\n", */
/*         line, p->macro_stack_ptr, p->macro_stack_size, p->alt_stack); */
/* } */

#if 0
static void print_csound_prsdata(CSOUND *csound, char *mesg, void *yyscanner)
{
    struct yyguts_t *yyg =(struct yyguts_t*)yyscanner;
    csound->DebugMsg(csound,"********* %s extra data ************", mesg);
    csound->DebugMsg(csound,"yyscanner = %p", yyscanner);
    csound->DebugMsg(csound,"yyextra_r = %p, yyin_r = %p, yyout_r = %p,"
                     " yy_buffer_stack_top = %d",
           yyg->yyextra_r, yyg->yyin_r,yyg->yyout_r, yyg->yy_buffer_stack_top);
    csound->DebugMsg(csound,"yy_buffer_stack_max = %d1, yy_buffer_stack = %p, "
                     "yy_hold_char = %d '%c'",
           yyg->yy_buffer_stack_max, yyg->yy_buffer_stack, yyg->yy_hold_char,
           yyg->yy_hold_char);
    csound->DebugMsg(csound,"yy_n_chars = %d, yyleng_r = %d, yy_c_buf_p = %p %c",
           yyg->yy_n_chars, yyg->yyleng_r, yyg->yy_c_buf_p, *yyg->yy_c_buf_p);
    csound->DebugMsg(csound,"yy_init = %d, yy_start = %d, "
                     "yy_did_buffer_switch_on_eof = %d",
           yyg->yy_init, yyg->yy_start, yyg->yy_did_buffer_switch_on_eof);
    csound->DebugMsg(csound,"yy_start_stack_ptr = %d,"
                     " yy_start_stack_depth = %d, yy_start_stack = %p",
           yyg->yy_start_stack_ptr, yyg->yy_start_stack_depth, yyg->yy_start_stack);

    csound->DebugMsg(csound,"yy_last_accepting_state = %d, "
                     "yy_last_accepting_cpos = %p %c",
           yyg->yy_last_accepting_state, yyg->yy_last_accepting_cpos,
                     *yyg->yy_last_accepting_cpos);
    csound->DebugMsg(csound,"yylineno_r = %d, yy_flex_debug_r = %d, "
                     "yytext_r = %p \"%s\", yy_more_flag = %d, yy_more_len = %d",
           yyg->yylineno_r, yyg->yy_flex_debug_r, yyg->yytext_r, yyg->yytext_r,
                     yyg->yy_more_flag, yyg->yy_more_len);
    {
      PRS_PARM* pp = yyg->yyextra_r;
      printf("macros = %p, isIfndef = %d, isString = %d, line - %d loc = %d\n",
             pp->macros, pp->isIfndef, pp->isString, pp->line, pp->locn);
      printf
        ("llocn = %d dept=%d\n", pp->llocn, pp->depth);
    }
    csound->DebugMsg(csound,"*********\n");
}
#endif
