%top{

/* ex: set ro ft=c:
 * !!!!!!!   DO NOT EDIT THIS FILE   !!!!!!!
 *
 * This file is generated automatically by the Parrot build process
 * from the file compilers/pirc/new/pir.l.
 *
 * Any changes made here will be lost!
 *
*/

/* HEADERIZER HFILE: none */
/* HEADERIZER STOP */

#ifndef __STDC_VERSION__
#  define __STDC_VERSION__ 0
#endif

#ifndef YY_NO_UNISTD_H
#  define YY_NO_UNISTD_H
#endif

/* prevent warnings on undefined #defines: */
#ifndef YY_MAIN
#  define YY_MAIN 0
#endif

#ifndef YY_ALWAYS_INTERACTIVE
#  define YY_ALWAYS_INTERACTIVE 0
#endif

#include <stdlib.h> /* for size_t */
typedef size_t yy_size_t;
#define YY_TYPEDEF_YY_SIZE_T

}

%{

/*
 * $Id: pir.l 41681 2009-10-04 11:07:09Z kjs $
 * Copyright (C) 2007-2009, Parrot Foundation.
 */

/*

=head1 NAME

pir.l - implementation of the lexical analyzer of the PIR assembly language.

=head1 DESCRIPTION

This file implements the lexical analyzer of the PIR assembly language.
The macro layer is implemented here as well. Heredocs and C<.include>
are processed by a different preprocessor; see F<hdocprep.l>.

=cut

*/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "pirparser.h"
#include "pircompiler.h"
#include "pirmacro.h"
#include "pirerr.h"

#include "parrot/parrot.h"

/* define the type of the extra field in the yyscan_t object that is passed around;
 * this is the lexer_state structure, defined in "pircompiler.h"
 */
#define YY_EXTRA_TYPE  struct lexer_state *

/* accessor methods for setting and getting the column; flex doesn't generate
 * prototypes for these for some reason. It's probably a missing feature, so
 * let's do that here and now.
 */
extern int  yypirget_column(yyscan_t yyscanner);
extern void yypirset_column(int col, yyscan_t yyscanner);

#ifdef _WIN32
/* warning C4018: "signed/unsigned mismatch" */
#  pragma warning (disable:4018)
/* warning C4244: "conversion from 'int' to 'yytype_int16' */
#  pragma warning (disable:4244)

#else
/* is this possible on other platforms? */

#endif

void include_file(yyscan_t yyscanner, char const * const filename);
char *expand_macro(yyscan_t yyscanner, macro_def * const macro, macro_param * args);

/* static prototypes */
static int is_pir_directive(char * const text);
static macro_table *pop_macro_table(lexer_state * const lexer);
static macro_table *peek_macro_table(lexer_state * const lexer);
static char * munge_id(char const * const id, lexer_state * const lexer);

static void save_filestate(yyscan_t yyscanner);
static int restore_filestate(yyscan_t yyscanner);

%}

ALPHA          [a-zA-Z_]
DIGIT          [0-9]
DIGITS         {DIGIT}+
ALNUM          {ALPHA}|{DIGIT}

IDENT          {ALPHA}{ALNUM}*

DOT            [.]
HEX            0[xX][0-9A-Fa-f]+
OCT            0[oO][0-7]+
BIN            0[bB][01]+
WS             [\t\f\r\x1a ]
EOL            \r?\n

SIGN           [-+]
BIGINT         {SIGN}?{DIGITS}"L"
FLOATNUM       {SIGN}?(({DIGITS}{DOT}{DIGIT}*|{DOT}{DIGITS})([eE]{SIGN}?{DIGITS})?|{DIGITS}[eE]{SIGN}?{DIGITS})

DQ_STRING       \"(\\.|[^"\\\n])*\"
SQ_STRING       \'[^'\n]*\'
Q_STRING       {SQ_STRING}|{DQ_STRING}

ENCCHAR         {ALPHA}|{DIGIT}|"-"
ENCCHARS        {ENCCHAR}*
ENC             {ALPHA}{ENCCHARS}":"
CHARSET         {ENC}
ENCODING        {ENC}

REGISTER       "$"[SNIP]{DIGITS}

/* make sure yytext is a pointer */
%pointer

/* slightly more efficient when this option is set; our parser is not interactive anyway. */
%option never-interactive

/* define output file */
%option outfile="pirlexer.c"

/* name of header file */
%option header-file="pirlexer.h"

/* prefix on exported functions. */
%option prefix="yypir"

/* we don't use unput, so don't generate it */
%option nounput

/* use flex' built-in capability for line counting */
%option yylineno

/* make the scanner re-entrant */
%option reentrant

/* needed for bison interaction. */
%option bison-bridge

/* make yywrap() always return true. */
%option noyywrap

/* always show warnings if something's wrong with our spec. */
%option warn

/* create a scanner in debug mode; XXX remove this in production build. */
%option debug

/* we're pushing and popping lexer states, so we need a stack */
%option stack

/* override memory allocation function with Parrot's memory functions;
 * don't generate built-ins.
 */
%option noyyalloc
%option noyyrealloc
%option noyyfree

/* XXX document, and possibly rename, these states, when are they used? */


%x MACROHEAD

%x MACROBODY

/* The MACROLOCAL state is used ONLY when parsing ".macro_local <type> <ident>". */
%x MACROLOCAL

/* The MACROLABEL state is used ONLY when parsing ".macro_label <ident>". */
%x MACROLABEL

/* The MACROEXPAND state is entered when we find a dot-identifier (e.g. ".foo"); */
%x MACROEXPAND

%x MACROCONST

/* note that SCANSTR is an inclusive state, purely to override default action for <<EOF>> */
%s SCANSTR

%s SCANMACRO

%x STRINGEXPAND

/* The SPECIALSTART state is only used when the parsing starts, and only to
 * read a single character; we're not interested in the character, but we want
 * a way for the lexer to check the PASM flag (indicating a PASM file), and this
 * check must only be done once (it's a waste to do it for each token). The
 * read character will be pushed back into the input stream immediately, and
 * a token will be returned, indicating it's a PASM or PIR file.
 */
%x SPECIALSTART

/* The PASM state is an exclusive state, recognizing ONLY PASM tokens. */
%x PASM


%%


<SPECIALSTART>.|\n    { /* only when the scanning starts, is this state used. Only a single
                         * character is read, pushed back, and then, depending on the
                         * lexer flags, either PASM or PIR mode (INITIAL state) is activated.
                         * The alternative (documented in Bison's info) would be to have a
                         * check on some variable, which is written right after the %% marker
                         * above. However, that code would be executed on every call to the
                         * lexer, which is a bit of a waste.
                         */
                        yyless(0); /* put everything back. It's only 1 character anyway. */
                        yy_pop_state(yyscanner); /* SPECIALSTART was pushed on state stack. */

                        if (TEST_FLAG(yypirget_extra(yyscanner)->flags, LEXER_FLAG_PASMFILE)) {
                            BEGIN(PASM);
                            return TK_PASM_MARKER_START;
                        }
                        else {
                            BEGIN(INITIAL); /* this is the normal case. */
                            return TK_PIR_MARKER_START;
                        }

                      }

{WS}              { /* ignore whitespace */ }

[#].*{EOL}{WS}*   { /* ignore line comments */ }

{EOL}[\t\r\n ]*   { /* a set of continuous newlines yields a single newline token. */
                    int index        = 0;
                    int num_newlines = 0;

                    /* count number of newlines in the matched text;
                     * iterate explicitly using an index is somewhat safer
                     * than with a character pointer, as you'd have to
                     * check for (*iter != '\0'), not (iter != NULL) for
                     * some reason. Just do it safely, and hope the compiler
                     * optimizes this a bit.
                     */
                    while (index < yyleng) {
                        if (yytext[index] == '\n')
                            ++num_newlines;

                        ++index;
                    }
                    /* pass the number of matched newlines */
                    yylval->ival = num_newlines;
                    return TK_NL;
                  }

">>>="            { return TK_ASSIGN_USHIFT; }
">>>"             { return TK_USHIFT; }
">>="             { return TK_ASSIGN_RSHIFT; }
">>"              { return TK_RSHIFT; }
"<<"              { return TK_LSHIFT; }
"=>"              { return TK_ARROW; }
"=="              { return TK_EQ; }
"!="              { return TK_NE; }
"<="              { return TK_LE; }
">="              { return TK_GE; }
"<"               { return TK_LT; }
">"               { return TK_GT; }

"//"              { return TK_FDIV; }
"&&"              { return TK_AND; }
"||"              { return TK_OR; }
"~~"              { return TK_XOR; }

"+"               { return '+'; }
"%"               { return '%'; }
"*"               { return '*'; }
"/"               { return '/'; }
"!"               { return '!'; }
"~"               { return '~'; }
"-"               { return '-'; }
"("               { return '('; }
")"               { return ')'; }
","               { return ','; }
"["               { return '['; }
"]"               { return ']'; }

{WS}"."{WS}       { /* if the dot is surrounded by whitespace, it's a concatenation operator */
                    return TK_CONC;
                  }


"="               { return '='; }
";"               { return ';'; }

"+="              { return TK_ASSIGN_INC; }
"-="              { return TK_ASSIGN_DEC; }
"/="              { return TK_ASSIGN_DIV; }
"*="              { return TK_ASSIGN_MUL; }
"%="              { return TK_ASSIGN_MOD; }
"**="             { return TK_ASSIGN_POW; }
"|="              { return TK_ASSIGN_BOR; }
"&="              { return TK_ASSIGN_BAND; }
"//="             { return TK_ASSIGN_FDIV; }
"~="              { return TK_ASSIGN_BNOT; }
".="              { return TK_ASSIGN_CONC; }

"if"              { return TK_IF; }
"goto"            { return TK_GOTO; }
"unless"          { return TK_UNLESS; }
"null"            { return TK_NULL; }

"int"             { return TK_INT; }
"num"             { return TK_NUM; }
"pmc"             { return TK_PMC; }
"string"          { return TK_STRING; }

".annotate"       { return TK_ANNOTATE; }
".set_arg"        { return TK_SET_ARG; }
".const"          { return TK_CONST; }
".end"            { return TK_END; }
".file"           { return TK_FILE; }

".get_results"    { return TK_GET_RESULTS; }
".globalconst"    { return TK_GLOBALCONST; }
".HLL"            { return TK_HLL; }
".invocant"       { return TK_INVOCANT; }
".lex"            { return TK_LEX; }
".line"           { return TK_LINE; }
".loadlib"        { return TK_LOADLIB; }
".local"          { return TK_LOCAL; }

".meth_call"      { return TK_METH_CALL; }
".namespace"      { return TK_NAMESPACE; }
".nci_call"       { return TK_NCI_CALL; }
".param"          { return TK_PARAM; }
".begin_call"     { return TK_BEGIN_CALL; }
".begin_return"   { return TK_BEGIN_RETURN; }
".begin_yield"    { return TK_BEGIN_YIELD; }
".call"           { return TK_CALL; }
".end_call"       { return TK_END_CALL; }
".end_return"     { return TK_END_RETURN; }
".end_yield"      { return TK_END_YIELD; }
".get_result"     { return TK_GET_RESULT; }
".return"         { return TK_RETURN; }
".sub"            { return TK_SUB; }
".yield"          { return TK_YIELD; }
".set_return"     { return TK_SET_RETURN; }
".set_yield"      { return TK_SET_YIELD; }
".tailcall"       { return TK_TAILCALL; }


".endm"|".macro_local"|".macro_label"  { /* make sure these are not used outside macro defs */
                    yypirerror(yyscanner, yyget_extra(yyscanner),
                               "cannot use '%s' outside of macro definitions", yytext);
                  }



":anon"           { return TK_FLAG_ANON; }
":init"           { return TK_FLAG_INIT; }
":load"           { return TK_FLAG_LOAD; }
":postcomp"       { return TK_FLAG_POSTCOMP; }
":immediate"      { return TK_FLAG_IMMEDIATE; }
":main"           { return TK_FLAG_MAIN; }
":method"         { return TK_FLAG_METHOD; }
":lex"            { return TK_FLAG_LEX; }
":outer"          { return TK_FLAG_OUTER; }
":vtable"         { return TK_FLAG_VTABLE; }
":multi"          { return TK_FLAG_MULTI; }
":subid"          { return TK_FLAG_SUBID; }
":instanceof"     { return TK_FLAG_INSTANCEOF; }
":nsentry"        { return TK_FLAG_NSENTRY; }

":unique_reg"     { return TK_FLAG_UNIQUE_REG; }
":optional"       { return TK_FLAG_OPTIONAL; }
":opt_flag"       { return TK_FLAG_OPT_FLAG; }
":slurpy"         { return TK_FLAG_SLURPY; }
":named"          { return TK_FLAG_NAMED; }
":flat"           { return TK_FLAG_FLAT; }
":invocant"       { return TK_FLAG_INVOCANT; }
":lookahead"      { return TK_FLAG_LOOKAHEAD; }

":"{IDENT}        { yypirerror(yyscanner, yyget_extra(yyscanner),
                               "unrecognized flag: '%s'", yytext);
                  }

{DQ_STRING}       { /* XXX this is a bit hacky. First the string is unescaped, but that
                     * returns a STRING * object; that's not what we want at this point.
                     * So, convert it back to a C string, and return that. Later, that
                     * string is converted into a STRING again, so this must be fixed.
                     * Preferably there would be an unescape function returning a C-string.
                     */
                    lexer_state * const lexer = yyget_extra(yyscanner);

                    STRING *pstr = Parrot_str_unescape(lexer->interp,
                                                           yytext + 1, '"', "ascii");

                    char *str = Parrot_str_to_cstring(lexer->interp, pstr);

                    yylval->sval = str;
                    
                    /* store the STRING in lexer's sval buffer; once PIRC is doing
                    only STRINGs, it can be stored in yylval.sval. */
                    lexer->sval = pstr;
                    
                    return TK_STRINGC;
                  }

{SQ_STRING}       { /* copy the string, remove the quotes. */
                    lexer_state * const lexer = yyget_extra(yyscanner);
                    
                    STRING *str = Parrot_str_unescape(lexer->interp, yytext + 1, '\'', "ascii");
                    lexer->sval = str;
                    
                    yylval->sval = dupstrn(lexer, yytext + 1, yyleng - 2);

                    
                    return TK_STRINGC;
                  }

{CHARSET}{DQ_STRING}  { /* XXX these double-quoted strings are not unescaped (yet) */
                    /* parse yytext, which contains the charset, a ':', and the quoted string */
                    char        *colon = strchr(yytext, ':');
                    lexer_state *lexer = yyget_extra(yyscanner);
                    ucstring    *ustr  = (ucstring *)pir_mem_allocate(lexer, sizeof (ucstring));
                    /* copy the charset part */
                    ustr->charset      = dupstrn(lexer, yytext, colon - yytext);
                    /* the colon becomes a NULL character to end the encoding string */
                    *colon = '\0';
                    /* copy the string contents, strip the quotes. Example:
                     *   iso-8859-1:"hi there"
                     *   123456789012345678901
                     *             ^
                     *          (colon)
                     * colon points to the colon, add 2 to point to the first character
                     * in the string ('h'). yyleng is 20 in the example, subtract
                     * string length of the charset part (10) and 3 for the quotes and colon.
                     */

                    ustr->contents = dupstrn(lexer, colon + 2, yyleng - strlen(ustr->charset) - 3);

                    ustr->encoding = NULL;

                    /* fprintf(stderr, "ucstring: [%s]:[%s]\n", ustr->charset, ustr->contents); */

                    yylval->ustr = ustr;
                    return TK_USTRINGC;
                  }

{ENCODING}{CHARSET}{DQ_STRING} { /* XXX these double-quoted strings are not unescaped (yet) */
                    /* parse yytext, which contains the encoding, a ':', a charset,
                     * a ':', and the quoted string
                     */
                    char        *colon1 = strchr(yytext, ':');
                    char        *colon2;
                    lexer_state *lexer = yyget_extra(yyscanner);
                    ucstring    *ustr  = (ucstring *)pir_mem_allocate(lexer, sizeof (ucstring));

                    /* yytext has the following structure:
                     *
                     * <charset><encoding><contents>
                     *
                     *   utf:unicode:"hi there"
                     *   1234567890123456789012
                     *      ^       ^
                     *   (colon1) (colon2)
                     *
                     * colon1 points to the first colon, colon2 points to the second.
                     */

                    /* copy the encoding part */
                    ustr->encoding = dupstrn(lexer, yytext, colon1 - yytext);

                    /* look for the second colon after this one */
                    colon2 = strchr(colon1 + 1, ':');
                    /* the colon becomes a NULL character to end the encoding string */
                    *colon1 = '\0';

                    ustr->charset  = dupstrn(lexer, colon1 + 1, colon2 - colon1 - 1);

                    /* start copying after the second colon, and skip the quote as well.
                     * yyleng contains the total length, substract length of encoding,
                     * length of charset, 2 quotes, and 2 colons (hence -4).
                     */
                    ustr->contents = dupstrn(lexer, colon2 + 2,
                                 yyleng - strlen(ustr->charset) - strlen(ustr->encoding) - 4);

                    /* fprintf(stderr, "ucstring: [%s]:[%s]:[%s]\n", ustr->encoding, ustr->charset,
                                   ustr->contents);
                    */

                    yylval->ustr = ustr;
                    return TK_USTRINGC;

                  }


"$P"{DIGIT}+      { yylval->ival = atoi(yytext + 2); return TK_PREG; }
"$S"{DIGIT}+      { yylval->ival = atoi(yytext + 2); return TK_SREG; }
"$N"{DIGIT}+      { yylval->ival = atoi(yytext + 2); return TK_NREG; }
"$I"{DIGIT}+      { yylval->ival = atoi(yytext + 2); return TK_IREG; }

{IDENT}":"        { /* make the label Id available in the parser. remove the ":" first. */
                    lexer_state * const lexer = yyget_extra(yyscanner);
                    STRING *str = Parrot_str_new(lexer->interp, yytext, yyleng - 1);
                    lexer->sval = str;
                    
                    yylval->sval = dupstrn(yyget_extra(yyscanner), yytext, yyleng - 1);
                    return TK_LABEL;
                  }

[SNIP]{DIGIT}+    { /* give a warning when using PASM registers as PIR identifiers */
                    lexer_state * const lexer = yyget_extra(yyscanner);

                    if (TEST_FLAG(lexer->flags, LEXER_FLAG_WARNINGS))
                        pirwarning(lexer, yypirget_lineno(yyscanner),
                                   "Use of PASM register identifier ('%s') "
                                   "is not encouraged for readable code", yytext);

                    yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                    return TK_IDENT;
                  }

{IDENT}           { /* identifier; can be a global (sub or const), local or parrot op */
                    lexer_state * const lexer = yyget_extra(yyscanner);
                    constdecl   * const c = find_global_constant(lexer, yytext);

                    if (c) { /* it's  a global const */
                        switch (c->type) {
                            case INT_VAL:
                                yylval->ival = c->val.ival;
                                return TK_INTC;
                            case STRING_VAL:
                                yylval->sval = c->val.sval;
                                return TK_STRINGC;
                            case NUM_VAL:
                                yylval->dval = c->val.nval;
                                return TK_NUMC;
                            case USTRING_VAL:
                                yylval->sval = c->val.ustr->contents;
                                return TK_USTRINGC;
                            default:
                                panic(lexer, "unsupported constant type");
                                break;
                        }
                    }
                    lexer->sval = Parrot_str_new(lexer->interp, yytext, yyleng);
                    					
                    yylval->sval = dupstr(lexer, yytext);
                    return TK_IDENT;
                  }

{FLOATNUM}        { yylval->dval = atof(yytext); return TK_NUMC; }
{SIGN}?{DIGITS}   { yylval->ival = atoi(yytext); return TK_INTC; }
{HEX}             { yylval->ival = atoi(yytext); return TK_INTC; }
{BIN}             { yylval->ival = atoi(yytext); return TK_INTC; }
{OCT}             { yylval->ival = atoi(yytext); return TK_INTC; }

"."/[\'\"$a-zA-Z] { /* Make sure the dot is followed by a character that
                     * starts a method object. $ for registers,
                     * quotes for quoted strings, and letters for identifiers.
                     *
                     */
                     return '.';
                  }

{WS}"."[^{WS}]    { yypirerror(yyscanner, yyget_extra(yyscanner),
                    "no space allowed before a methodcall dot, "
                    "or space expected after the '.' operator");
                    return '.';
                  }

%{

/* SCANSTR<<EOF>> and SCANMACRO<<EOF>> must be defined before <INTIAL><<EOF>>,
 * because they are %s states, which means they are inclusive, not exclusive states.
 * An inclusive scanner state will recognize all other tokens defined in <INITIAL>
 * state, but they are used here to override the action when scanning <INITIAL><<EOF>>.
 */

/* SCANSTR and SCANMACRO are states that are active when expanding a macro_const
 * or a macro, respectively. When expanding a macro_const, no new macro_table
 * is created, which acts as a local symbol table for parameters. Hence, there
 * are two different <<EOF>> actions for them.
 */

%}

<SCANSTR><<EOF>>   { /* switch back from .macro_const buffer to file. */
                     lexer_state * const lexer = yyget_extra(yyscanner);
                     yy_pop_state(yyscanner);
                     yy_switch_to_buffer(lexer->buffer, yyscanner);
                   }

<SCANMACRO><<EOF>> { /* override the default <<EOF>> action; go back to normal state and
                      * switch back to the saved file.
                      */
                     lexer_state * const lexer = yyget_extra(yyscanner);
                     macro_table * const table = pop_macro_table(lexer);

                     yy_pop_state(yyscanner); /* pop off the SCANMACRO scanner state */

                     /* switch back to whatever buffer we were scanning before we started
                      * to read the macro's buffer.
                      */
                     yy_switch_to_buffer(table->prev_buff, yyscanner);

                     /* restore line number */
                     yyset_lineno(table->lineno, yyscanner);

                     /* restore current scope id */
                     lexer->unique_id = table->scopeno;

                     delete_macro_table(table);

                   }

<SCANMACRO>{IDENT}"@"  { /* when scanning a macro body, the @ marker indicates the {IDENT} must
                          * be munged.
                          */
                            lexer_state * const lexer = yyget_extra(yyscanner);
                            char const  * const id    = dupstrn(lexer, yytext, yyleng - 1);
                            yylval->sval              = munge_id(id, lexer);
                            return TK_IDENT;
                       }

<INITIAL><<EOF>>  { /* end of file, stop scanning. */
                    yyterminate();
                  }

.                 { /* any character not covered in the rules above is an error. */
                    yypirerror(yyscanner, yyget_extra(yyscanner),
                               "unexpected character: '%c'", yytext[0]);
                  }


%{

/*****************************************************************************
*
*  the following rules implement the macro layer.
*
*****************************************************************************/
%}


%{

/********************** .macro_const ****************************************/

%}

".macro_const"               {
                               yy_push_state(MACROCONST, yyscanner);
                               return TK_MACRO_CONST;
                             }

<MACROCONST>{IDENT}          {
                               yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                               return TK_IDENT;
                             }

<MACROCONST>{REGISTER}|{FLOATNUM}|{OCT}|({SIGN}?{DIGITS})|{HEX}|{BIN}|{Q_STRING} {
                               /* only these tokens can be macro constant values */
                               yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                               yy_pop_state(yyscanner);
                               return TK_MACRO_CONST_VAL;
                             }

<MACROCONST>{WS}             { /* ignore whitespace */ }
<MACROCONST>.                {
                               yypirerror(yyscanner, yyget_extra(yyscanner),
                                          "unknown character: '%c'", yytext[0]);
                             }
<MACROCONST><<EOF>>          {
                               yypirerror(yyscanner, yyget_extra(yyscanner),
                                          "read end of file during .macro_const definition");
                             }

%{

/**************** macro header (.macro <ident> <parameters> ) ***************/

%}

".macro"                     { /* start a macro definition */
                               yy_push_state(MACROHEAD, yyscanner);
                               return TK_MACRO;
                             }

<MACROHEAD>{WS}              { /* ignore whitespace */ }
<MACROHEAD>{IDENT}           {
                               yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                               return TK_IDENT;
                             }

<MACROHEAD>"("               { return '('; }
<MACROHEAD>")"               { return ')'; }
<MACROHEAD>","               { return ','; }

<MACROHEAD>{EOL}[\t\r\n ]*   { /* a set of continuous newlines yields a single newline token. */
                               yy_pop_state(yyscanner); /* remove MACROHEAD state */
                               yy_push_state(MACROBODY, yyscanner); /* enter MACROBODY state */
                               return TK_NL;
                             }
%{

/******************************* macro expansion ***************************/

/*
 * Only identifiers, constants and registers are recognized.
 */

%}


"."{IDENT}                   { /* .foo; it can be a macro, macro_local, or just $P0.foo(),
                                * but we need to check that.
                                */
                               lexer_state * const lexer = yyget_extra(yyscanner);
                               macro_def   * const macro = find_macro(lexer->macros, yytext + 1);

                               if (macro == NULL) { /* it's not a macro */

                                   /* it may be a .macro_local */
                                   macro_table *table = peek_macro_table(lexer);

                                   if (table->thismacro != NULL) { /* not expanding a macro */

                                       if (is_macro_local(table->thismacro, yytext + 1)) {
                                           yylval->sval = munge_id(yytext + 1, lexer);
                                           return TK_IDENT;
                                       }
                                       /* else fall through */
                                   }

                                   /* table->thismacro == NULL || is_macro_local() == FALSE */
                                   yyless(1); /* return all but first character */
                                   return '.';

                               }
                               else { /* it's a macro */
                                   /* fprintf(stderr, "found .ident: %s\n", yytext + 1); */

                                   /* store current buffer at this point, because we'll be
                                    * scanning a string buffer later on. Saving must be done
                                    * in the lexer specification, otherwise YY_CURRENT_BUFFER
                                    * cannot be used (it's a macro).
                                    */
                                   lexer->buffer = YY_CURRENT_BUFFER;

                                   /* if it's a .macro, (not .macro_const), then go into
                                    * MACROEXPAND state, to correctly parse the arguments.
                                    * For .macro_const, we don't want this, because a .macro_const
                                    * is expanded here, hidden from the parser.
                                    */
                                   if (macro->takes_args) {
                                       yylval->mval = macro;
                                       yy_push_state(MACROEXPAND, yyscanner);
                                       return TK_MACRO_IDENT;
                                   }
                                   else { /* expand the .macro_const here; no need for
                                           * adding this to the grammar. */
                                       /* goto SCANSTR state, and scan macro->body */
                                       yy_push_state(SCANSTR, yyscanner);
                                       yy_scan_string(macro->body, yyscanner);
                                   }
                               }
                             }

<MACROEXPAND>"."{IDENT}      { /* expand a .macro_const or parameter in argument list */
                               lexer_state * const lexer = yyget_extra(yyscanner);
                               macro_def   * const macro = find_macro(lexer->macros, yytext + 1);

                               if (macro) {
                                   /* .foo( .bar() ) is not allowed; that's too fancy for PIR. */
                                   if (macro->takes_args) {
                                      /*
                                       yypirerror(yyscanner, lexer, "cannot expand a .macro"
                                                  "in another .macro's argument list");

                                       */

                                       yylval->mval = macro;
                                       /* yy_push_state(MACROEXPAND, yyscanner);
                                       */
                                      return TK_MACRO_IDENT;
                                   }
                                   else { /* we do allow expansion of .macro_consts as macro args */
                                       lexer->buffer = YY_CURRENT_BUFFER;
                                       yy_push_state(STRINGEXPAND, yyscanner);
                                       yy_scan_string(macro->body, yyscanner);
                                   }
                               }
                               else
                                   yypirerror(yyscanner, lexer, "'%s' is not a macro", yytext + 1);
                             }

<MACROEXPAND>{IDENT}         {
                               yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                               return TK_MACRO_ARG_IDENT;
                             }

<MACROEXPAND>{REGISTER}      {
                               yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                               return TK_MACRO_ARG_OTHER;
                             }

<MACROEXPAND>{WS}            { /* ignore whitespace */ }
<MACROEXPAND>","             { return ','; }
<MACROEXPAND>"("             { return '('; }
<MACROEXPAND>")"             {
                               yy_pop_state(yyscanner); /* leave MACROEXPAND state */
                               return ')';
                             }

<STRINGEXPAND,MACROEXPAND>{FLOATNUM}|{OCT}|({SIGN}?{DIGITS})|{HEX}|{BIN}|{Q_STRING} {
                               yylval->sval = dupstr(yyget_extra(yyscanner), yytext);
                               return TK_MACRO_ARG_OTHER;
                             }

<STRINGEXPAND><<EOF>>        {
                               lexer_state * const lexer = yyget_extra(yyscanner);
                               yy_pop_state(yyscanner);
                               yy_switch_to_buffer(lexer->buffer, yyscanner);
                             }

<MACROEXPAND>"{"             { return '{'; }
<MACROEXPAND>"}"             { return '}'; }

<MACROEXPAND>{EOL}[\t\r\n ]* { yylval->sval = "\n"; return TK_NL; }

<MACROEXPAND>.               { yypirerror(yyscanner, yyget_extra(yyscanner),
                                          "unknown character in macro expansion: %c", yytext[0]);
                             }


%{

/******************************** .macro_local *****************************/

%}

<MACROBODY>".local"         { /* give a warning if the right flag is set */
                              /*
                              lexer_state * const lexer = yyget_extra(yyscanner);
                              if (TEST_FLAG(lexer->flags, LEXER_FLAG_WARNINGS))
                                  pirwarning(lexer, yyget_lineno(yyscanner),
                                             "use of .local in macros prevents multiple "
                                             "usage of macro in one sub. Use .macro_local instead");
                              return TK_LOCAL;
                              XXX we should disallow .local altogether in macros.
                              Doesn't make sense to use it.
                              */
                            }


<MACROBODY>".macro_local"   {
                              yy_push_state(MACROLOCAL, yyscanner);
                              return TK_MACRO_LOCAL;
                            }

<MACROLOCAL>"int"           { return TK_INT; }
<MACROLOCAL>"pmc"           { return TK_PMC; }
<MACROLOCAL>"num"           { return TK_NUM; }
<MACROLOCAL>"string"        { return TK_STRING; }

<MACROLOCAL>{IDENT}         { /* normal .macro_local */
                              lexer_state * const lexer = yyget_extra(yyscanner);
                              /* reserve space for {IDENT}, the @ marker and the NULL char. */
                              char * temp = (char *)mem_sys_allocate((yyleng + 2) * sizeof (char));
                              /* stick a special marker "@" so we can recognize this as a id
                               * that must be munged.
                               */
                              sprintf(temp, "%s@", yytext);
                              yylval->sval = temp;

                              /* declare it as a macro local symbol */
                              declare_macro_local(CURRENT_MACRO(lexer), dupstr(lexer, yytext));
                              return TK_MACRO_LOCAL_ID;
                            }

<MACROLOCAL>"$"{IDENT}      { /* declare a .macro_local based on a parameter */
                              lexer_state * const lexer = yyget_extra(yyscanner);

                              char * temp = (char *)mem_sys_allocate((yyleng + 2) * sizeof (char));
                              /* stick a "@" after, so it can be recognized later */
                              sprintf(temp, "%s@", yytext);
                              yylval->sval = temp;

                              /* declare the {IDENT} part as a local */
                              declare_macro_local(CURRENT_MACRO(lexer), dupstr(lexer, yytext + 1));
                              return TK_MACRO_LOCAL_ID;

                            }

<SCANMACRO>".$"{IDENT}      { /* .$foo */
                              lexer_state * const lexer = yyget_extra(yyscanner);
                              macro_table * const table = peek_macro_table(lexer);
                              macro_def   * const param = find_macro(table, yytext + 2);

                              if (param == NULL)
                                  yypirerror(yyscanner, lexer, "Cannot expand '%s', as it is "
                                             "not a macro parameter", yytext);
                              else {
                                  unsigned len = strlen(param->body);
                                  /* length includes quotes, so substract 2, + 1 for NULL char. */
                                  char *unquoted = (char *)mem_sys_allocate_zeroed(
                                                                        (len - 1) * sizeof (char));
                                  /* remove the quotes */
                                  if (param->body[0] != '"')
                                      yypirerror(yyscanner, lexer,
                                         "value of macro argument '%s' must be a quoted string",
                                         yytext);

                                  strncpy(unquoted, param->body + 1, len - 2);
                                  yylval->sval = munge_id(unquoted, lexer);

                                  mem_sys_free(unquoted);
                                  return TK_IDENT;

                              }

                            }

<SCANMACRO>"$"{IDENT}"@"   { /* expanding a .macro_local using a macro parameter value */
                             lexer_state * const lexer     = yyget_extra(yyscanner);
                             char  const * const paramname = dupstrn(lexer, yytext + 1, yyleng - 2);
                             macro_def   * const param     = find_macro(lexer->macros, paramname);

                             if (param == NULL) {
                                yypirerror(yyscanner, lexer, "Cannot expand %s; '%s' was not "
                                           "declared as a macro parameter", yytext, paramname);
                             }
                             else {
                                unsigned  len      = strlen(param->body);
                                char     *unquoted = (char *)mem_sys_allocate_zeroed(
                                                                      (len - 1) * sizeof (char));

                                /* unquote the string */
                                strncpy(unquoted, param->body + 1, len - 2);

                                yylval->sval       = munge_id(unquoted, lexer);
                                mem_sys_free(unquoted);
                             }
                             /* fprintf(stderr, "scanmacro:$id@: [%s]\n", yylval->sval); */

                             return TK_IDENT;
                           }

<MACROLOCAL>{WS}            { /* ignore whitespace */ }

<MACROLOCAL>{EOL}[\t\r\n ]* { /* newline after .macro_local <type> <ident> line */
                              yy_pop_state(yyscanner);
                              return TK_NL;
                            }

<MACROLOCAL>.               { /* this state is only used for declaring .macro_locals */
                              yypirerror(yyscanner, yyget_extra(yyscanner),
                                 "unknown character '%c' when declaring .macro_local", yytext[0]);
                            }
%{

/****************************** .macro_label ********************************/

%}

<MACROBODY>".macro_label"   {
                              yy_push_state(MACROLABEL, yyscanner);
                              return TK_MACRO_LABEL;
                            }

<MACROLABEL>"$"?{IDENT}":"  { /* if the "$" is there, it's a macro label using a macro
                               * parameter's value; otherwise it's a normal macro label
                               */
                              lexer_state * const lexer = yyget_extra(yyscanner);
                              char * temp = (char *)pir_mem_allocate(lexer,
                                                                     (yyleng + 2) * sizeof (char));
                              /* stick a special marker "@" so we can recognize this as a label
                               * that must be munged.
                               */
                              strncpy(temp, yytext, yyleng - 1);
                              strcpy(temp + yyleng - 1, "@:");

                              yylval->sval = temp;
                              declare_macro_local(CURRENT_MACRO(lexer), dupstr(lexer, yytext));
                              return TK_MACRO_LABEL_ID;
                            }


<MACROLABEL>{EOL}[\t\r\n ]* { /* the newline character after a ".macro_label $foo:" declaration */
                              yy_pop_state(yyscanner); /* leave MACROLABEL state */
                              return TK_NL;
                            }


<SCANMACRO>{IDENT}"@:"      { /* scan a label when expanding a buffer; declared as .macro_label */
                              lexer_state * const lexer = yyget_extra(yyscanner);
                              char const  * const label = dupstrn(lexer, yytext, yyleng - 2);
                              yylval->sval = munge_id(label, lexer);
                              return TK_LABEL;
                            }

<SCANMACRO>"$"{IDENT}"@:"  { /* scan a label when expanding macro; was a macro parameter */
                             lexer_state * const lexer     = yyget_extra(yyscanner);
                             char const  * const paramname = dupstrn(lexer, yytext + 1, yyleng - 3);
                             macro_def   * const param     = find_macro(lexer->macros, paramname);

                             if (param == NULL) {
                                 yypirerror(yyscanner, lexer, "Cannot expand %s; '%s' was not "
                                            "declared as a macro parameter", yytext, paramname);
                             }
                             else {
                                 unsigned len   = strlen(param->body);
                                 char *unquoted = (char *)mem_sys_allocate_zeroed(
                                                                       (len - 1) * sizeof (char));
                                 /* unquote the string */
                                 if (param->body[0] != '"')
                                     yypirerror(yyscanner, lexer,
                                         "value of macro argument '%s' must be a quoted string",
                                         yytext);

                                 strncpy(unquoted, param->body + 1, len - 2);
                                 yylval->sval = munge_id(unquoted, lexer);
                                 mem_sys_free(unquoted);
                             }
                             return TK_LABEL;
                           }


%{

/*************************** macro body scanning (storing) ****************************/

%}

<MACROBODY>{EOL}[\t\r\n ]*   { store_macro_char(CURRENT_MACRO(yyget_extra(yyscanner)), '\n'); }

<MACROBODY>".endm"           {
                               yy_pop_state(yyscanner); /* leave MACROBODY state */
                               return TK_ENDM;
                             }

<MACROBODY>.                 { /* store everything else */
                               store_macro_char(CURRENT_MACRO(yyget_extra(yyscanner)), yytext[0]);
                             }

<MACROBODY><<EOF>>           { /* catch run-away macro bodys */
                               yypirerror(yyscanner, yyget_extra(yyscanner),
                                          "read end of file while reading macro body");
                             }


%{ /*
{WS}"."           {
                    yypirerror(yyscanner, yyget_extra(yyscanner),
                          "ambiguous '.'; must be enclosed in space on both sides or none at all.");
                    return TK_CONC;
                  }


"."{WS}           {
                    yypirerror(yyscanner, yyget_extra(yyscanner),
                          "ambiguous '.'; must be enclosed in space on both sides or none at all.");
                    return TK_CONC;
                  }

       */
%}

%{

/* PASM tokens.
 *
 * do not mix with other tokens, using <state1,state2> notation.
 * This becomes unmanageable
 */
%}

<PASM>","               { return ','; }
<PASM>"["               { return '['; }
<PASM>"]"               { return ']'; }

<PASM>":main"           { return TK_FLAG_MAIN; }
<PASM>":load"           { return TK_FLAG_LOAD; }
<PASM>":init"           { return TK_FLAG_INIT; }
<PASM>":anon"           { return TK_FLAG_ANON; }
<PASM>":postcomp"       { return TK_FLAG_POSTCOMP; }
<PASM>":immediate"      { return TK_FLAG_IMMEDIATE; }

<PASM>".pcc_sub"        { return TK_PCC_SUB; }
<PASM>".lex"            { return TK_LEX; }
<PASM>".namespace"      { return TK_NAMESPACE; }

<PASM>".macro"          {
                          yy_push_state(MACROHEAD, yyscanner);
                          return TK_MACRO;
                        }

<PASM>".macro_const"    {
                          yy_push_state(MACROCONST, yyscanner);
                          return TK_MACRO_CONST;
                        }

<PASM>".line"           { return TK_LINE; }
<PASM>".file"           { return TK_FILE; }


<PASM>"."{IDENT}        { /* macro expansion in PASM mode. */
                          lexer_state * const lexer = yyget_extra(yyscanner);
                          macro_def   * const macro = find_macro(lexer->macros, yytext + 1);

                          if (macro == NULL) { /* it's not a macro */
                              yypirerror(yyscanner, lexer, "cannot find macro '%s'", yytext + 1);
                          }
                          else { /* it's a macro */
                              /* store current buffer at this point, because we'll be
                               * scanning a string buffer later on. Saving must be done
                               * in the lexer specification, otherwise YY_CURRENT_BUFFER
                               * cannot be used (it's a macro).
                               */
                              lexer->buffer = YY_CURRENT_BUFFER;

                              /* if it's a .macro, (not .macro_const), then go into
                               * MACROEXPAND state, to correctly parse the arguments.
                               * For .macro_const, we don't want this, because a .macro_const
                               * is expanded here, hidden from the parser.
                               */
                              if (macro->takes_args) {
                                  yylval->mval = macro;
                                  yy_push_state(MACROEXPAND, yyscanner);
                                  return TK_MACRO_IDENT;
                              }
                              else { /* expand the .macro_const here; no need for
                                      * adding this to the grammar. */
                                  /* goto SCANSTR state, and scan macro->body */
                                  yy_push_state(SCANSTR, yyscanner);
                                  yy_scan_string(macro->body, yyscanner);
                              }
                          }
                        }

<PASM>{IDENT}":"        { /* a label in PASM */
                          yylval->sval = dupstrn(yypirget_extra(yyscanner), yytext, yyleng - 1);
                          return TK_LABEL;
                        }

<PASM>{REGISTER}        { yypirerror(yyscanner, yypirget_extra(yyscanner),
                                     "symbolic registers are not allowed in PASM mode");
                        }
<PASM>"P"{DIGITS}       { yylval->ival = atoi(yytext + 1); return TK_PREG; }
<PASM>"N"{DIGITS}       { yylval->ival = atoi(yytext + 1); return TK_NREG; }
<PASM>"I"{DIGITS}       { yylval->ival = atoi(yytext + 1); return TK_IREG; }
<PASM>"S"{DIGITS}       { yylval->ival = atoi(yytext + 1); return TK_SREG; }

<PASM>{IDENT}           { /* can be a parrot op or a label; the check is done in the parser. */
                          yylval->sval = dupstr(yypirget_extra(yyscanner), yytext);
                          return TK_IDENT;
                        }

<PASM>{FLOATNUM}        { yylval->dval = atof(yytext); return TK_NUMC; }
<PASM>{SIGN}?{DIGITS}   { yylval->ival = atoi(yytext); return TK_INTC; }
<PASM>{HEX}             { yylval->ival = atoi(yytext); return TK_INTC; }
<PASM>{BIN}             { yylval->ival = atoi(yytext); return TK_INTC; }
<PASM>{OCT}             { yylval->ival = atoi(yytext); return TK_INTC; }

<PASM>{Q_STRING}        { /* copy the string, remove the quotes. */
                          yylval->sval = dupstrn(yyget_extra(yyscanner), yytext + 1, yyleng - 2);
                          return TK_STRINGC;
                        }

<PASM>{WS}              { /* ignore whitespace */ }

<PASM>{EOL}[\t\r\n ]*   { return TK_NL; }

<PASM>.                 { yypirerror(yyscanner, yypirget_extra(yyscanner),
                                     "unrecognized character: %c", yytext[0]);
                        }
<PASM><<EOF>>           { yyterminate(); }

%%



/*

=head1 FUNCTIONS

Helper functions for macro expansion. These function are only used by the lexer,
so keep them local to this file.

=over 4

=cut

*/


/*

=item C<void
init_scanner_state(yyscan_t yyscanner)>

Initializes C<yyscanner> to the SPECIALSTART state, which can then decide
whether to scan PASM or PIR tokens. State identifiers (e.g. SPECIALSTART)
are not exported (private to the generated lexer), so this function wraps
the call to yy_push_state().

=cut

*/
void
init_scanner_state(yyscan_t yyscanner) {
    yy_push_state(SPECIALSTART, yyscanner);
}


/*

=item C<static macro_table *
peek_macro_table(lexer_state * const lexer)>

Return the top of the macro_table stack; this is the currently
active macro_table object.

=cut

*/
PARROT_WARN_UNUSED_RESULT
static macro_table *
peek_macro_table(lexer_state * const lexer) {
    return lexer->macros;
}

/*

=item C<static char *
munge_id(char const * const id, lexer_state * const lexer)>

Generate an identifier based on a macro label or local expansion.

A label expansion looks like ".$<LABEL>", from which a label identifier is
generated, formatted as: "$macro_<MACRO>_<LABEL>_<SCOPE>".

In this format, C<MACRO> refers to the macro name of the macro in which
the .macro_local or .macro_label was defined; C<LABEL> refers to the runtime
value of the label or local variable (this can be a macro's argument's value).
C<SCOPE> refers to the unique scope ID of this macro expansion.

=cut

*/
PARROT_MALLOC
PARROT_CANNOT_RETURN_NULL
PARROT_WARN_UNUSED_RESULT
static char *
munge_id(char const * const id, lexer_state * const lexer) {
    /* the format of the generated label; note that it has a $ character,
     * which is not allowed in normal PIR identifiers. This way, you cannot
     * use the generated names outside of macros. We dont't want people
     * write outside of macros:
     *
     *   macro_foo_bar_1 = 42
     *
     * Hence the $ character.
     */
    char       *       munged_id;
    char const * const format        = "$macro_%s_%s_%d"; /* 15 characters */
    int  const         format_length = strlen(format);


    macro_table *table = peek_macro_table(lexer);

    /* calculate length of the generated label: length of macro name,
     * plus length of label name.
     */
    int length = format_length;

    /* add length of the macro name */
    length += strlen(table->thismacro->name);

    /* add length of unique id to total length */
    length += lexer->num_digits;

    /* add length of the actual label id */
    length += strlen(id);

    munged_id = (char *)pir_mem_allocate_zeroed(lexer, (length + 1) * sizeof (char));

    sprintf(munged_id, format, table->thismacro->name, id, lexer->unique_id);

    return munged_id;
}



/*

=item C<static macro_table *
pop_macro_table(lexer_state * const lexer)>

Pop off the current macro symbol table from the
macro symbol table stack and return it.

=cut

*/
PARROT_IGNORABLE_RESULT
PARROT_CANNOT_RETURN_NULL
static macro_table *
pop_macro_table(lexer_state * const lexer) {
    macro_table *popped = lexer->macros;
    lexer->macros       = popped->prev;
    return popped;
}


/*

=item C<static void
push_macro_table(lexer_state * const lexer, macro_table * const table)>

Push macro_table C<table> onto the macro scope stack. Any attempt to find
a macro definition will start in C<table>. If not found, the stack will be walked
down (recursing into older entries).

=cut

*/
static void
push_macro_table(lexer_state * const lexer, macro_table * const table) {
    table->prev   = lexer->macros;
    lexer->macros = table;
}

/*

=item C<static void
update_unique_id(lexer_state * const lexer)>

Update the unique ID generator; the number of characters that the new number
is taking in a string is recalculated, as the unique IDs are used in string
context.

=cut

*/
static void
update_unique_id(lexer_state * const lexer) {
    /* each expansion has a unique id that is used for label/local munging */
    ++lexer->id_gen;
    /* Count number of digits:
     * log10 returns a double, get the part before the dot (so, "3.14" -> "3")
     * using the floor() function.
     * log10(1000) -> 3, so add 1 more digit.
     */
    lexer->num_digits = (int)floor(log10(lexer->id_gen)) + 1;
}


/*

=item C<static void
expand(yyscan_t yyscanner, macro_def * const macro, list * args, lexer_state * const lexer)>

Expand the specified macro (or constant). The current yy_buffer_state is saved, as well
as the current scope ID (for macro expansions; each expansion has its own unique scope ID);
as well as the current line number. The macro_table, which defines the macro parameters/arguments,
gets a reference to the macro definition that is being expanded; that's the C<thismacro> field
of the C<macro_table> structure.

An argument count check is done to make sure there's exactly enough arguments for the defined
parameters. Then, the macro definition's body (a string buffer) is scanned (instead of the
file that we were scanning).

The function returns the body of the macro being expanded.

=cut

*/
char *
expand_macro(yyscan_t yyscanner, macro_def * const macro, macro_param * args) {
    /* construct a map data structure that maps the argument values to the parameter names */
    /* enter the parameters as temporary symbols (.macro_const) */
    lexer_state *lexer        = yypirget_extra(yyscanner);
    macro_table *macro_params = new_macro_table(lexer->macros);
    macro_param *params       = macro->parameters;

    /* push the new macro_table, acting as a local symbol scope */
    push_macro_table(lexer, macro_params);

    /* save the current line, unique_id and current buffer in the macro_table,
     * so they can be restored when the macro_table is popped from the stack.
     */
    macro_params->lineno     = yyget_lineno(yyscanner);
    /* save current scope id */
    macro_params->scopeno    = lexer->unique_id;
    macro_params->prev_buff  = lexer->buffer; /* cannot use YY_CURRENT_BUFFER here, but it was
                                                 stored in lexer->buffer anyway, so store that. */
    macro_params->thismacro  = macro; /* let the macro_table point to the macro-definition that
                                         is being expanded; this allows access to macro name etc. */

    while (params && args) {
        new_macro_const(macro_params, params->name, args->name, yyget_lineno(yyscanner));
      /*  fprintf(stderr, "defining parameter '%s' as '%s'\n", params->name, args->name); */
        params = params->next;
        args   = args->next;
    }

    /* check for both conditions; either can be non-null, indicating an error.
     * If both are null, then all went ok.
     */
    if (params != NULL)  /* args must be null, so too few arguments */
        yypirerror(yyscanner, lexer, "Too few arguments for macro expansion '%s'", macro->name);

    if (args != NULL)  /* params must be null, so too many arguments */
        yypirerror(yyscanner, lexer, "Too many arguments for macro expansion '%s'", macro->name);


    update_unique_id(lexer);
    lexer->unique_id = lexer->id_gen;

    /* switch to SCANMACRO state, and tell the lexer to get
     * next tokens from the string buffer by calling yy_scan_string().
     */
    yy_push_state(SCANMACRO, yyscanner);
    yy_scan_string(macro->body, yyscanner);
    /* update the line number in the yyscanner so that any error message occuring
     * refers to the bad line in the macro definition. Note that this must done
     * **after** the call to yy_scan_string(), which sets the line number to 1.
     */
    yyset_lineno(macro->linedefined, yyscanner);

    return macro->body;
}


/* override Flex generated memory functions: all memory allocated
 * by Flex goes through Parrot's memory allocators.
 */

/*

=item C<void *
yyalloc(yy_size_t bytes, yyscan_t yyscanner)>

Allocate C<bytes> of memory; the C<yyscanner> argument is not used.

=cut

*/
PARROT_MALLOC
void *
yyalloc(yy_size_t bytes, yyscan_t yyscanner) {
    return mem_sys_allocate(bytes);
}

/*

=item C<void *
yyrealloc(void * ptr, yy_size_t bytes, yyscan_t yyscanner)>

Reallocate memory pointed to by C<ptr>. The new memory region is C<bytes> in size;
the C<yyscanner> argument is not used.

=cut

*/
void *
yyrealloc(void * ptr, yy_size_t bytes, yyscan_t yyscanner) {
    return mem_sys_realloc(ptr, bytes);
}

/*

=item C<void
yyfree(void *ptr, yyscan_t yyscanner)>

Free memory pointed to by C<ptr>. The C<yyscanner> argument is not used,
but part of the signature as it is a Flex-generated function.

=cut

*/
void
yyfree(void *ptr, yyscan_t yyscanner) {
    mem_sys_free(ptr);
}




/*

=back

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
