/*
 * NASM-compatible re2c lexer
 *
 *  Copyright (C) 2001-2007  Peter Johnson
 *
 *  Portions based on re2c's example code.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <util.h>

#include <libyasm.h>

#include "modules/parsers/nasm/nasm-parser.h"
#include "modules/preprocs/nasm/nasm.h"


#define YYCURSOR        cursor
#define YYLIMIT         (s->lim)
#define YYMARKER        (s->ptr)
#define YYFILL(n)       {}

#define RETURN(i)       {s->cur = cursor; parser_nasm->tokch = s->tok[0]; \
                         return i;}

#define SCANINIT()      {s->tok = cursor;}

#define TOK             ((char *)s->tok)
#define TOKLEN          (size_t)(cursor-s->tok)


/* starting size of string buffer */
#define STRBUF_ALLOC_SIZE       128

/* string buffer used when parsing strings/character constants */
static YYCTYPE *strbuf = NULL;

/* length of strbuf (including terminating NULL character) */
static size_t strbuf_size = 0;

static int linechg_numcount;

/*!re2c
  any = [\001-\377];
  digit = [0-9];
  iletter = [a-zA-Z];
  bindigit = [01_];
  octdigit = [0-7_];
  hexdigit = [0-9a-fA-F_];
  ws = [ \t\r];
  quot = ["'];
*/

static int
handle_dot_label(YYSTYPE *lvalp, char *tok, size_t toklen, size_t zeropos,
                 yasm_parser_nasm *parser_nasm)
{
    /* check for special non-local labels like ..start */
    if (tok[zeropos+1] == '.') {
        lvalp->str_val = yasm__xstrndup(tok+zeropos+(parser_nasm->tasm?2:0),
            toklen-zeropos-(parser_nasm->tasm?2:0));
        /* check for special non-local ..@label */
        if (lvalp->str_val[zeropos+2] == '@')
            return NONLOCAL_ID;
        return SPECIAL_ID;
    }
    if (parser_nasm->masm && tok[zeropos] == '.') {
        lvalp->str_val = yasm__xstrndup(tok + zeropos, toklen - zeropos);
        return SPECIAL_ID;
    }
    if (parser_nasm->tasm && (!tasm_locals || 
                (tok[zeropos] == '.' &&
                 tok[zeropos+1] != '@' && tok[zeropos+2] != '@'))) {
        /* no locals on Tasm without the 'locals' directive */
        /* .foo is never local either, but .@@foo may be (local structure
         * members) */
        lvalp->str_val = yasm__xstrndup(tok + zeropos, toklen - zeropos);
        return SPECIAL_ID;
    }
    if (!parser_nasm->locallabel_base) {
        lvalp->str_val = yasm__xstrndup(tok+zeropos, toklen-zeropos);
        yasm_warn_set(YASM_WARN_GENERAL,
                      N_("no non-local label before `%s'"),
                      lvalp->str_val);
    } else {
        size_t len = toklen - zeropos + parser_nasm->locallabel_base_len;
        lvalp->str_val = yasm_xmalloc(len + 1);
        strcpy(lvalp->str_val, parser_nasm->locallabel_base);
        strncat(lvalp->str_val, tok+zeropos, toklen-zeropos);
        lvalp->str_val[len] = '\0';
    }

    return LOCAL_ID;
}

int
nasm_parser_lex(YYSTYPE *lvalp, yasm_parser_nasm *parser_nasm)
{
    yasm_scanner *s = &parser_nasm->s;
    YYCTYPE *cursor = s->cur;
    YYCTYPE endch;
    size_t count;
    YYCTYPE savech;

    /* Handle one token of lookahead */
    if (parser_nasm->peek_token != NONE) {
        int tok = parser_nasm->peek_token;
        *lvalp = parser_nasm->peek_tokval;  /* structure copy */
        parser_nasm->tokch = parser_nasm->peek_tokch;
        parser_nasm->peek_token = NONE;
        return tok;
    }

    /* Catch EOL (EOF from the scanner perspective) */
    if (s->eof && cursor == s->eof)
        return 0;

    /* Jump to proper "exclusive" states */
    switch (parser_nasm->state) {
        case DIRECTIVE:
            goto directive;
        case SECTION_DIRECTIVE:
            goto section_directive;
        case DIRECTIVE2:
            goto directive2;
        case LINECHG:
            goto linechg;
        case LINECHG2:
            goto linechg2;
        default:
            break;
    }

scan:
    SCANINIT();
    if (*cursor == '\0')
        goto endofinput;

    /*!re2c
        /* standard decimal integer */
        digit+ {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            lvalp->intn = yasm_intnum_create_dec(TOK);
            s->tok[TOKLEN] = savech;
            RETURN(INTNUM);
        }
        /* 10010011b - binary number */

        [01] bindigit* 'b' {
            s->tok[TOKLEN-1] = '\0'; /* strip off 'b' */
            lvalp->intn = yasm_intnum_create_bin(TOK);
            RETURN(INTNUM);
        }

        /* 777q or 777o - octal number */
        [0-7] octdigit* [qQoO] {
            s->tok[TOKLEN-1] = '\0'; /* strip off 'q' or 'o' */
            lvalp->intn = yasm_intnum_create_oct(TOK);
            RETURN(INTNUM);
        }

        /* 0AAh form of hexidecimal number */
        digit hexdigit* 'h' {
            s->tok[TOKLEN-1] = '\0'; /* strip off 'h' */
            lvalp->intn = yasm_intnum_create_hex(TOK);
            RETURN(INTNUM);
        }

        /* $0AA and 0xAA forms of hexidecimal number */
        (("$" digit) | '0x') hexdigit+ {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            if (s->tok[1] == 'x' || s->tok[1] == 'X')
                /* skip 0 and x */
                lvalp->intn = yasm_intnum_create_hex(TOK+2);
            else
                /* don't skip 0 */
                lvalp->intn = yasm_intnum_create_hex(TOK+1);
            s->tok[TOKLEN] = savech;
            RETURN(INTNUM);
        }

        /* floating point value */
        digit+ "." digit* ('e' [-+]? digit+)? {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            lvalp->flt = yasm_floatnum_create(TOK);
            s->tok[TOKLEN] = savech;
            RETURN(FLTNUM);
        }

        /* string/character constant values */
        quot {
            endch = s->tok[0];
            goto stringconst;
        }

        /* %line linenum+lineinc filename */
        "%line" {
            parser_nasm->state = LINECHG;
            linechg_numcount = 0;
            RETURN(LINE);
        }

        /* size specifiers */
        'byte'          { lvalp->int_info = 8; RETURN(SIZE_OVERRIDE); }
        'hword'         {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)/2;
            RETURN(SIZE_OVERRIDE);
        }
        'word'          {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch);
            RETURN(SIZE_OVERRIDE);
        }
        'dword' | 'long'        {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*2;
            RETURN(SIZE_OVERRIDE);
        }
        'qword'         {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*4;
            RETURN(SIZE_OVERRIDE);
        }
        'tword'         { lvalp->int_info = 80; RETURN(SIZE_OVERRIDE); }
        'dqword'        {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*8;
            RETURN(SIZE_OVERRIDE);
        }
        'oword'        {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*8;
            RETURN(SIZE_OVERRIDE);
        }
        'yword'        {
            lvalp->int_info = 256;
            RETURN(SIZE_OVERRIDE);
        }

        /* pseudo-instructions */
        'db'            {
            lvalp->int_info = 8;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'dhw'           {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)/2;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'dw'            {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch);
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'dd'            {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*2;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'dq'            {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*4;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'dt'            {
            lvalp->int_info = 80;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'ddq'           {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*8;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'do'           {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*8;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }
        'dy'           {
            lvalp->int_info = 256;
            parser_nasm->state = INSTRUCTION;
            RETURN(DECLARE_DATA);
        }

        'resb'          {
            lvalp->int_info = 8;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'reshw'         {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)/2;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'resw'          {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch);
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'resd'          {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*2;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'resq'          {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*4;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'rest'          {
            lvalp->int_info = 80;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'resdq'         {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*8;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'reso'         {
            lvalp->int_info = yasm_arch_wordsize(p_object->arch)*8;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }
        'resy'         {
            lvalp->int_info = 256;
            parser_nasm->state = INSTRUCTION;
            RETURN(RESERVE_SPACE);
        }

        'incbin'        { RETURN(INCBIN); }

        'equ'           { RETURN(EQU); }

        'times'         { RETURN(TIMES); }

        'seg'           { RETURN(SEG); }
        'wrt'           { RETURN(WRT); }

        'abs'           { RETURN(ABS); }
        'rel'           { RETURN(REL); }

        'nosplit'       { RETURN(NOSPLIT); }
        'strict'        { RETURN(STRICT); }

        /* operators */
        "<<"                    { RETURN(LEFT_OP); }
        ">>"                    { RETURN(RIGHT_OP); }
        "//"                    { RETURN(SIGNDIV); }
        "%%"                    { RETURN(SIGNMOD); }
        "$$"                    { RETURN(START_SECTION_ID); }
        [-+|^*&/%~$():=,\[?]    { RETURN(s->tok[0]); }
        "]"                     { RETURN(s->tok[0]); }

        /* local label (.label) */
        ("." | "@@") [a-zA-Z0-9_$#@~.?]+ {
            RETURN(handle_dot_label(lvalp, TOK, TOKLEN, 0, parser_nasm));
        }

        /* forced identifier */
        "$" [a-zA-Z0-9_$#@~.?]+ {
            if (TOK[1] == '.' ||
                    (parser_nasm->tasm && TOK[1] == '@' && TOK[2] == '@')) {
                /* handle like .label */
                RETURN(handle_dot_label(lvalp, TOK, TOKLEN, 1, parser_nasm));
            }
            lvalp->str_val = yasm__xstrndup(TOK+1, TOKLEN-1);
            RETURN(ID);
        }

        /* identifier that may be a register, instruction, etc. */
        [a-zA-Z_?@][a-zA-Z0-9_$#@~.?]* {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            if (parser_nasm->state != INSTRUCTION) {
                uintptr_t prefix;
                switch (yasm_arch_parse_check_insnprefix
                        (p_object->arch, TOK, TOKLEN, cur_line, &lvalp->bc,
                         &prefix)) {
                    case YASM_ARCH_INSN:
                        parser_nasm->state = INSTRUCTION;
                        s->tok[TOKLEN] = savech;
                        RETURN(INSN);
                    case YASM_ARCH_PREFIX:
                        lvalp->arch_data = prefix;
                        s->tok[TOKLEN] = savech;
                        RETURN(PREFIX);
                    default:
                        break;
                }
            }
            switch (yasm_arch_parse_check_regtmod
                    (p_object->arch, TOK, TOKLEN, &lvalp->arch_data)) {
                case YASM_ARCH_REG:
                    s->tok[TOKLEN] = savech;
                    RETURN(REG);
                case YASM_ARCH_SEGREG:
                    s->tok[TOKLEN] = savech;
                    RETURN(SEGREG);
                case YASM_ARCH_TARGETMOD:
                    s->tok[TOKLEN] = savech;
                    RETURN(TARGETMOD);
                case YASM_ARCH_REGGROUP:
                    if (parser_nasm->masm) {
                        s->tok[TOKLEN] = savech;
                        RETURN(REGGROUP);
                    }
                default:
                    break;
            }
            if (parser_nasm->masm) {
               if (!yasm__strcasecmp(TOK, "offset")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(OFFSET);
                }
            } else if (parser_nasm->tasm) {
                if (!yasm__strcasecmp(TOK, "shl")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(LEFT_OP);
                }
                if (!yasm__strcasecmp(TOK, "shr")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(RIGHT_OP);
                }
                if (!yasm__strcasecmp(TOK, "and")) {
                    s->tok[TOKLEN] = savech;
                    RETURN('&');
                }
                if (!yasm__strcasecmp(TOK, "or")) {
                    s->tok[TOKLEN] = savech;
                    RETURN('|');
                }
                if (!yasm__strcasecmp(TOK, "not")) {
                    s->tok[TOKLEN] = savech;
                    RETURN('~');
                }
                if (!yasm__strcasecmp(TOK, "low")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(LOW);
                }
                if (!yasm__strcasecmp(TOK, "high")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(HIGH);
                }
                if (!yasm__strcasecmp(TOK, "offset")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(OFFSET);
                }
                if (!yasm__strcasecmp(TOK, "fword")) {
                    s->tok[TOKLEN] = savech;
                    lvalp->int_info = yasm_arch_wordsize(p_object->arch)*2;
                    RETURN(SIZE_OVERRIDE);
                }
                if (!yasm__strcasecmp(TOK, "df")) {
                    s->tok[TOKLEN] = savech;
                    lvalp->int_info = yasm_arch_wordsize(p_object->arch)*3;
                    parser_nasm->state = INSTRUCTION;
                    RETURN(DECLARE_DATA);
                }
                if (!yasm__strcasecmp(TOK, "label")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(LABEL);
                }
                if (!yasm__strcasecmp(TOK, "dup")) {
                    s->tok[TOKLEN] = savech;
                    RETURN(DUP);
                }
            }
            /* Propagate errors in case we got a warning from the arch */
            yasm_errwarn_propagate(parser_nasm->errwarns, cur_line);
            /* Just an identifier, return as such. */
            s->tok[TOKLEN] = savech;
            lvalp->str_val = yasm__xstrndup(TOK, TOKLEN);
            RETURN(ID);
        }

        ";" (any \ [\000])*     { goto scan; }

        ws+                     { goto scan; }

        [\000]                  { goto endofinput; }

        any {
            yasm_warn_set(YASM_WARN_UNREC_CHAR,
                          N_("ignoring unrecognized character `%s'"),
                          yasm__conv_unprint(s->tok[0]));
            goto scan;
        }
    */

    /* %line linenum+lineinc filename */
linechg:
    SCANINIT();
    if (*cursor == '\0')
        goto endofinput;

    /*!re2c
        digit+ {
            linechg_numcount++;
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            lvalp->intn = yasm_intnum_create_dec(TOK);
            s->tok[TOKLEN] = savech;
            RETURN(INTNUM);
        }

        [\000] { goto endofinput; }

        "+" {
            RETURN(s->tok[0]);
        }

        ws+ {
            if (linechg_numcount == 2) {
                parser_nasm->state = LINECHG2;
                goto linechg2;
            }
            goto linechg;
        }

        any {
            yasm_warn_set(YASM_WARN_UNREC_CHAR,
                          N_("ignoring unrecognized character `%s'"),
                          yasm__conv_unprint(s->tok[0]));
            goto linechg;
        }
    */

linechg2:
    SCANINIT();
    if (*cursor == '\0')
        goto endofinput;

    /*!re2c
        [\000] { goto endofinput; }

        "\r" { goto linechg2; }

        (any \ [\000])+ {
            parser_nasm->state = LINECHG;
            lvalp->str_val = yasm__xstrndup(TOK, TOKLEN);
            RETURN(FILENAME);
        }
    */

    /* directive: [name value] */
directive:
    SCANINIT();
    if (*cursor == '\0')
        goto endofinput;

    /*!re2c
        [\]\000] { goto endofinput; }

        [a-zA-Z_][a-zA-Z_0-9]* {
            lvalp->str_val = yasm__xstrndup(TOK, TOKLEN);
            if (yasm__strcasecmp(lvalp->str_val, "section") == 0 ||
                yasm__strcasecmp(lvalp->str_val, "segment") == 0)
                parser_nasm->state = SECTION_DIRECTIVE;
            else
                parser_nasm->state = DIRECTIVE2;
            RETURN(DIRECTIVE_NAME);
        }

        any {
            yasm_warn_set(YASM_WARN_UNREC_CHAR,
                          N_("ignoring unrecognized character `%s'"),
                          yasm__conv_unprint(s->tok[0]));
            goto directive;
        }
    */

    /* section directive (the section name portion thereof) */
section_directive:
    SCANINIT();
    if (*cursor == '\0')
        goto endofinput;

    /*!re2c
        [a-zA-Z0-9_$#@~.?-]+ {
            lvalp->str.contents = yasm__xstrndup(TOK, TOKLEN);
            lvalp->str.len = TOKLEN;
            parser_nasm->state = DIRECTIVE2;
            RETURN(STRING);
        }

        quot            {
            parser_nasm->state = DIRECTIVE2;
            endch = s->tok[0];
            goto stringconst;
        }

        ws+             {
            parser_nasm->state = DIRECTIVE2;
            goto section_directive;
        }

        [\]\000]        { goto endofinput; }

        any {
            yasm_warn_set(YASM_WARN_UNREC_CHAR,
                          N_("ignoring unrecognized character `%s'"),
                          yasm__conv_unprint(s->tok[0]));
            goto section_directive;
        }
    */

    /* inner part of directive */
directive2:
    SCANINIT();
    if (*cursor == '\0')
        goto endofinput;

    /*!re2c
        /* standard decimal integer */
        digit+ {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            lvalp->intn = yasm_intnum_create_dec(TOK);
            s->tok[TOKLEN] = savech;
            RETURN(INTNUM);
        }
        /* 10010011b - binary number */

        [01] bindigit* 'b' {
            s->tok[TOKLEN-1] = '\0'; /* strip off 'b' */
            lvalp->intn = yasm_intnum_create_bin(TOK);
            RETURN(INTNUM);
        }

        /* 777q or 777o - octal number */
        [0-7] octdigit* [qQoO] {
            s->tok[TOKLEN-1] = '\0'; /* strip off 'q' or 'o' */
            lvalp->intn = yasm_intnum_create_oct(TOK);
            RETURN(INTNUM);
        }

        /* 0AAh form of hexidecimal number */
        digit hexdigit* 'h' {
            s->tok[TOKLEN-1] = '\0'; /* strip off 'h' */
            lvalp->intn = yasm_intnum_create_hex(TOK);
            RETURN(INTNUM);
        }

        /* $0AA and 0xAA forms of hexidecimal number */
        (("$" digit) | '0x') hexdigit+ {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            if (s->tok[1] == 'x' || s->tok[1] == 'X')
                /* skip 0 and x */
                lvalp->intn = yasm_intnum_create_hex(TOK+2);
            else
                /* don't skip 0 */
                lvalp->intn = yasm_intnum_create_hex(TOK+1);
            s->tok[TOKLEN] = savech;
            RETURN(INTNUM);
        }

        /* string/character constant values */
        quot {
            endch = s->tok[0];
            goto stringconst;
        }

        /* operators */
        "<<"                    { RETURN(LEFT_OP); }
        ">>"                    { RETURN(RIGHT_OP); }
        "//"                    { RETURN(SIGNDIV); }
        "%%"                    { RETURN(SIGNMOD); }
        [-+|^*&/%~$():=,\[]     { RETURN(s->tok[0]); }

        /* handle ] for directives */
        "]"                     { goto endofinput; }

        /* forced identifier; within directive, don't strip '$', this is
         * handled later.
         */
        "$" [a-zA-Z0-9_$#@~.?]+ {
            lvalp->str_val = yasm__xstrndup(TOK, TOKLEN);
            RETURN(ID);
        }

        /* identifier; within directive, no local label mechanism */
        [a-zA-Z_.?][a-zA-Z0-9_$#@~.?]* {
            savech = s->tok[TOKLEN];
            s->tok[TOKLEN] = '\0';
            switch (yasm_arch_parse_check_regtmod
                    (p_object->arch, TOK, TOKLEN, &lvalp->arch_data)) {
                case YASM_ARCH_REG:
                    s->tok[TOKLEN] = savech;
                    RETURN(REG);
                default:
                    s->tok[TOKLEN] = savech;
            }
            /* Propagate errors in case we got a warning from the arch */
            yasm_errwarn_propagate(parser_nasm->errwarns, cur_line);
            /* Just an identifier, return as such. */
            lvalp->str_val = yasm__xstrndup(TOK, TOKLEN);
            RETURN(ID);
        }

        ";" (any \ [\000])*     { goto directive2; }

        ws+                     { goto directive2; }

        [\000]                  { goto endofinput; }

        any {
            yasm_warn_set(YASM_WARN_UNREC_CHAR,
                          N_("ignoring unrecognized character `%s'"),
                          yasm__conv_unprint(s->tok[0]));
            goto scan;
        }
     */

    /* string/character constant values */
stringconst:
    strbuf = yasm_xmalloc(STRBUF_ALLOC_SIZE);
    strbuf_size = STRBUF_ALLOC_SIZE;
    count = 0;

stringconst_scan:
    SCANINIT();
    if (*cursor == '\0')
        goto stringconst_error;

    /*!re2c
        [\000]  { goto stringconst_error; }

        "''" | '""'     {
            if (endch != s->tok[0]) {
                strbuf[count++] = s->tok[0];
                if (count >= strbuf_size) {
                    strbuf = yasm_xrealloc(strbuf,
                                           strbuf_size + STRBUF_ALLOC_SIZE);
                    strbuf_size += STRBUF_ALLOC_SIZE;
                }
            } else if (!parser_nasm->tasm) {
                YYCURSOR--;
                goto stringconst_end;
            }
            strbuf[count++] = s->tok[0];
            if (count >= strbuf_size) {
                strbuf = yasm_xrealloc(strbuf, strbuf_size + STRBUF_ALLOC_SIZE);
                strbuf_size += STRBUF_ALLOC_SIZE;
            }
            goto stringconst_scan;
        }

        any     {
            if (s->tok[0] == endch)
                goto stringconst_end;

            strbuf[count++] = s->tok[0];
            if (count >= strbuf_size) {
                strbuf = yasm_xrealloc(strbuf, strbuf_size + STRBUF_ALLOC_SIZE);
                strbuf_size += STRBUF_ALLOC_SIZE;
            }

            goto stringconst_scan;
        }
    */

stringconst_error:
    yasm_error_set(YASM_ERROR_SYNTAX, N_("unterminated string"));

stringconst_end:
    strbuf[count] = '\0';
    lvalp->str.contents = (char *)strbuf;
    lvalp->str.len = count;
    RETURN(STRING);

endofinput:
    parser_nasm->state = INITIAL;
    RETURN(s->tok[0]);
}