Search
lxdream.org :: lxdream/src/tools/mdparse.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/mdparse.c
changeset 1105:73da8fd129fb
prev1104:700e16c321e5
next1296:30ecee61f811
author nkeynes
date Fri Feb 24 17:31:18 2012 +1000 (12 years ago)
permissions -rw-r--r--
last change Add some real option processing to genglsl and let it accept multiple glsl
input files (basically concatenate them together)
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * genmmio I/O register definition parser.
     5  *
     6  * Copyright (c) 2010 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    19 #include <assert.h>
    20 #include <stdlib.h>
    21 #include <stdio.h>
    22 #include <string.h>
    23 #include <unistd.h>
    24 #include <fcntl.h>
    25 #include <ctype.h>
    26 #include <errno.h>
    27 #include <sys/stat.h>
    28 #include <glib/gmem.h>
    30 #include "genmach.h"
    31 /*
    32  * Grammar:
    33  *
    34  * start: start register_block |  ;
    35  * register_block: 'registers' IDENTIFIER 'at' INTEGER opt_paramlist '{' register_list '}';
    36  * opt_paramlist: '(' param_list ')' | '(' ')' |  ;
    37  * param_list: param_list ',' param | param ;
    38  * param: access_param | fill_param | endian_param | mask_param ;
    39  * access_param: 'access' '=' 'any'  // Behaves as if it were memory
    40  *        | 'access' '=' 'nooffset' // Recognize any size access (zero-ext or trunc) as long as the address is exactly the same
    41  *        | 'access' '=' 'exact'    // Recognize only the exact access type at the exact address
    42  *        ;
    43  * fill_param: 'fill' '=' literal;
    44  * endian_param: 'endian' '=' 'little'
    45  *        | 'endian' '=' 'big'
    46  *        ;
    47  * mask_param: 'mask' '=' literal;
    48  *
    49  * register_list: register_list register_desc | ;
    50  * register_desc: INTEGER ':' mode_spec type_spec IDENTIFIER opt_paramlist [ '=' literal ] [ ACTION ]
    51  * mode_spec: 'const' | 'rw' | 'ro' | 'wo';
    52  * type_spec: 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64' | 'string' | 'cstring';
    53  * literal: INTEGER | STRING | 'undefined' ;
    54  *
    55  * C-style comments are recognized as such.
    56  */
    58 #define TOK_IDENTIFIER 1
    59 #define TOK_INTEGER    2
    60 #define TOK_STRING     3
    61 #define TOK_ACTION     4
    62 #define TOK_EOF        5
    63 #define TOK_ERROR      6
    64 #define TOK_LPAREN     7
    65 #define TOK_RPAREN     8
    66 #define TOK_LBRACE     9
    67 #define TOK_RBRACE    10
    68 #define TOK_COMMA     11
    69 #define TOK_COLON     12
    70 #define TOK_SEMI      13
    71 #define TOK_PERIOD    14
    72 #define TOK_RANGE     15
    73 #define TOK_LSQUARE   16
    74 #define TOK_RSQUARE   17
    75 #define TOK_EQUALS    18
    76 #define TOK_ACCESS    19
    77 #define TOK_AT        20
    78 #define TOK_ENDIAN    21
    79 #define TOK_FILL      22
    80 #define TOK_INCLUDE   23
    81 #define TOK_MASK      24
    82 #define TOK_REGISTERS 25
    83 #define TOK_SPACE     26
    84 #define TOK_STRIDE    27
    85 #define TOK_TEST      28
    86 #define TOK_TRACE     29
    87 #define TOK_UNDEFINED 30
    90 #define FIRST_KEYWORD TOK_ACCESS
    91 #define LAST_KEYWORD  TOK_UNDEFINED
    92 static const char *TOKEN_NAMES[] = {"NULL", "IDENTIFIER", "INTEGER", "STRING", "ACTION", "EOF", "ERROR",
    93         "(", ")", "{", "}", ",", ":", ";", ".", "..", "[", "]", "=",
    94         "ACCESS", "AT", "ENDIAN", "FILL", "INCLUDE", "MASK", "REGISTERS", "SPACE", "STRIDE", "TEST", "TRACE", "UNDEFINED" };
    95 static const char *MODE_NAMES[] = {"const", "rw", "ro", "wo", "mirror"};
    96 static const char *TYPE_NAMES[] = {"i8", "i16", "i32", "i64", "f32", "f64", "string"};
    97 static int TYPE_SIZES[] = {1,2,4,8,4,8,0};
    98 static const char *ENDIAN_NAMES[] = {"default", "little", "big"};
    99 static const char *ACCESS_NAMES[] = {"default", "any", "nooffset", "exact"};
   100 static const char *TRACE_NAMES[] = {"default", "off", "on"};
   102 #define elementsof(x) (sizeof(x)/sizeof(x[0]))
   104 typedef struct token_data {
   105     char *yytext;
   106     int yylength;
   107     int yyline;
   108     int yycol;
   109     union {
   110         long i;
   111         char *s;
   112     } v;
   113     int slen;
   114 } token_data;
   116 struct yystate {
   117     char *yybuffer;
   118     char *yyfilename;
   119     char *yyposn, *yylineposn, *yyend;
   120     int yylineno;
   121 };
   123 static GList *yyfile_stack = NULL;
   124 static struct yystate yystate;
   125 static struct token_data yytok;
   127 #define YYPARSE_ERROR( msg, ... ) \
   128     do { \
   129         fprintf( stderr, "Parse error in %s:%d:%d: " msg "\n", yystate.yyfilename, yytok.yyline, yytok.yycol, __VA_ARGS__ ); \
   130         exit(2); \
   131     } while(0)
   133 #define READ(x) \
   134 {   int _tok = iolex(x); \
   135     if( _tok != (x) ) { \
   136         YYPARSE_ERROR( "Expected %s but got %s", TOKEN_NAMES[x], TOKEN_NAMES[_tok] ); \
   137     } \
   138 }
   140 static int iolex( int expectToken );
   141 static int iolex_open( const char *filename );
   142 static void iolex_close();
   143 static int iolex_push( const char *filename );
   144 static int iolex_pop( );
   146 static inline char *yystrdup()
   147 {
   148     char *str = g_malloc0(yytok.yylength+1);
   149     memcpy( str, yytok.yytext, yytok.yylength);
   150     return str;
   151 }
   153 static inline int yystrcasecmp(const char *cmp)
   154 {
   155     int len = strlen(cmp);
   156     if( len != yytok.yylength ) {
   157         return yytok.yylength - len;
   158     }
   159     return strncasecmp(yytok.yytext, cmp, yytok.yylength);
   160 }
   162 static int yymatch( const char *arr[], unsigned numOptions )
   163 {
   164     for( unsigned i=0; i<numOptions; i++ ) {
   165         if( yystrcasecmp( arr[i] ) == 0 ) {
   166             return i;
   167         }
   168     }
   169     return -1;
   170 }
   172 static gint register_block_sort_cb( gconstpointer a, gconstpointer b )
   173 {
   174     struct regblock *blocka = (struct regblock *)a;
   175     struct regblock *blockb = (struct regblock *)b;
   176     return blocka->address - blockb->address;
   177 }
   179 static int register_ptr_sort_cb( const void *a, const void *b )
   180 {
   181     regdef_t *ptra = (regdef_t *)a;
   182     regdef_t *ptrb = (regdef_t *)b;
   183     if( (*ptra)->offset != (*ptrb)->offset )
   184         return (*ptra)->offset - (*ptrb)->offset;
   185     return (*ptra)->type - (*ptrb)->type;
   186 }
   188 static void ioparse_apval(int tok, union apval *apv, unsigned *numBytes)
   189 {
   190     if( tok == TOK_INTEGER ) {
   191         apv->i = yytok.v.i;
   192         if( *numBytes == 0 ) {
   193             YYPARSE_ERROR( "Expected string initializer but was an integer (0x%x)", yytok.v.i );
   194         }
   195     } else if( tok = TOK_STRING ) {
   196         if( *numBytes == 0 ) {
   197             *numBytes = yytok.slen;
   198             apv->s = yytok.v.s;
   199         } else {
   200             if( *numBytes != yytok.slen ) {
   201                 YYPARSE_ERROR( "Expected %d byte initializer but was %d", *numBytes, yytok.slen );
   202             }
   203             assert( *numBytes < sizeof(uint64_t) );
   204             apv->i = 0;
   205             /* FIXME: handle endian mismatches */
   206             memcpy( &apv->i, yytok.v.s, yytok.slen );
   207         }
   208     } else {
   209         YYPARSE_ERROR( "Expected literal (integer or string), but got %s", TOKEN_NAMES[tok] );
   210     }
   211 }
   213 static void ioparse_regflags( regflags_t flags, unsigned *numBytes )
   214 {
   215     do {
   216         int tok = iolex(TOK_RPAREN);
   217         switch(tok) {
   218         case TOK_FILL:
   219             READ(TOK_EQUALS);
   220             tok = iolex(TOK_INTEGER);
   221             flags->fillSizeBytes = 4;
   222             ioparse_apval( tok, &flags->fillValue, &flags->fillSizeBytes );
   223             break;
   224         case TOK_ACCESS:
   225             READ(TOK_EQUALS);
   226             READ(TOK_IDENTIFIER);
   227             flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES));
   228             if( flags->access == -1 ) {
   229                 YYPARSE_ERROR("Unknown access mode '%s'", yystrdup());
   230             }
   231             break;
   232         case TOK_MASK:
   233             if( numBytes ) {
   234                 READ(TOK_EQUALS);
   235                 tok = iolex(TOK_INTEGER);
   236                 ioparse_apval( tok, &flags->maskValue, numBytes );
   237             } else {
   238                 YYPARSE_ERROR("mask is not valid on a register block",0);
   239             }
   240             break;
   241         case TOK_ENDIAN:
   242             READ(TOK_EQUALS);
   243             READ(TOK_IDENTIFIER);
   244             flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES));
   245             if( flags->endian == -1 ) {
   246                 YYPARSE_ERROR("Unknown endianness '%s'", yystrdup());
   247             }
   248             break;
   249         case TOK_TRACE:
   250             READ(TOK_EQUALS);
   251             READ(TOK_IDENTIFIER);
   252             flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
   253             if( flags->traceFlag == -1 ) {
   254                 YYPARSE_ERROR("Unknown trace flag '%s'", yystrdup());
   255             }
   256             break;
   257         case TOK_TEST:
   258             READ(TOK_EQUALS);
   259             READ(TOK_IDENTIFIER);
   260             flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
   261             if( flags->testFlag == -1 ) {
   262                 YYPARSE_ERROR("Unknown test flag '%s'", yystrdup());
   263             }
   264             break;
   265         case TOK_COMMA:
   266             break;
   267         case TOK_RPAREN:
   268             return;
   269         default:
   270             YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]);
   271         }
   272     } while(1);
   273 }
   275 static void ioparse_regdef( struct regdef *reg )
   276 {
   277     reg->offset = yytok.v.i;
   278     reg->numElements = 1;
   279     reg->numBytes = 0;
   280     reg->initValue.i = 0;
   281     reg->flags.maskValue.i = -1;
   282     unsigned rangeOffset = reg->offset;
   284     int tok = iolex(TOK_COLON);
   285     if( tok == TOK_RANGE ) {
   286         READ(TOK_INTEGER);
   287         rangeOffset = yytok.v.i;
   288         if( rangeOffset < reg->offset ) {
   289             YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset );
   290         }
   291         READ(TOK_COLON);
   292     } else if( tok != TOK_COLON ) {
   293         YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] );
   294     }
   295     READ(TOK_IDENTIFIER);
   296     reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES));
   297     if( reg->mode == -1 ) {
   298         YYPARSE_ERROR( "Unknown register mode '%s'", yystrdup() );
   299     }
   300     if( reg->mode == REG_MIRROR ) {
   301         /* Mirror regions have a target range only */
   302         READ(TOK_INTEGER);
   303         reg->initValue.i = yytok.v.i;
   304         reg->type = REG_I8;
   305         tok = iolex(TOK_RANGE);
   306         if( tok == TOK_RANGE ) {
   307             READ(TOK_INTEGER);
   308             if( yytok.v.i < reg->initValue.i ) {
   309                 YYPARSE_ERROR( "Invalid mirror target range 0x%x..0x%x", reg->initValue.i, yytok.v.i );
   310             }
   311             reg->numBytes = yytok.v.i - reg->initValue.i + 1;
   312             tok = iolex(TOK_STRIDE);
   313         }
   314         if( tok == TOK_STRIDE ) {
   315             READ(TOK_INTEGER);
   316             reg->stride = yytok.v.i;
   317             tok = iolex(TOK_ACTION);
   318         } else {
   319             reg->stride = reg->numBytes;
   320         }
   322         if( tok != TOK_SEMI ) {
   323             YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] );
   324         }
   326         if( reg->stride < reg->numBytes ) {
   327             YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes );
   328         }
   330         if( rangeOffset != reg->offset ) {
   331             reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride;
   332         }
   334     } else {
   335         READ(TOK_IDENTIFIER);
   336         reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES));
   337         if( reg->type == -1 ) {
   338             YYPARSE_ERROR( "Unknown register type '%s'", yystrdup() );
   339         }
   340         reg->numBytes = TYPE_SIZES[reg->type];
   341         tok = iolex(TOK_IDENTIFIER);
   342         if( tok == TOK_IDENTIFIER ) {
   343             reg->name = yystrdup();
   344             tok = iolex(TOK_ACTION);
   345         }
   346         if( tok == TOK_STRING ) {
   347             reg->description = yytok.v.s;
   348             tok = iolex(TOK_ACTION);
   349         }
   350         if( tok == TOK_LPAREN ) {
   351             ioparse_regflags(&reg->flags, &reg->numBytes);
   352             tok = iolex(TOK_ACTION);
   353         }
   354         if( tok == TOK_EQUALS ) {
   355             tok = iolex(TOK_INTEGER);
   356             if( tok == TOK_UNDEFINED ) {
   357                 reg->initValue.i = 0xDEADBEEFDEADBEEF;
   358                 reg->initUndefined = TRUE;
   359             } else {
   360                 ioparse_apval(tok, &reg->initValue, &reg->numBytes);
   361             }
   362             tok = iolex(TOK_ACTION);
   363         } else if( reg->type == REG_STRING ) {
   364             YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')",0 );
   365         }
   366         if( tok == TOK_ACTION ) {
   367             // reg->action = yystrdup();
   368         } else if( tok != TOK_SEMI ) {
   369             YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] );
   370         }
   372         if( rangeOffset != reg->offset ) {
   373             reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes;
   374         }
   375     }
   377 }
   379 static struct regblock *ioparse_regblock( )
   380 {
   381     unsigned regsAllocSize = 128;
   382     struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
   383     block->numRegs = 0;
   385     READ(TOK_IDENTIFIER);
   386     block->name = yystrdup();
   387     int tok = iolex(TOK_AT);
   388     if( tok == TOK_STRING ) {
   389         block->description = yytok.v.s;
   390         tok = iolex(TOK_AT);
   391     }
   392     if( tok != TOK_AT ) {
   393         YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] );
   394     }
   395     READ(TOK_INTEGER);
   396     block->address = yytok.v.i;
   398     tok = iolex(TOK_LBRACE);
   399     if( tok == TOK_LPAREN) {
   400         ioparse_regflags(&block->flags, NULL);
   401         READ(TOK_LBRACE);
   402     } else if( tok != TOK_LBRACE ) {
   403         YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] );
   404     }
   406     tok = iolex(TOK_INTEGER);
   407     while( tok != TOK_RBRACE ) {
   408         if( tok != TOK_INTEGER ) {
   409             YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]);
   410         }
   411         struct regdef *regdef = g_malloc0(sizeof(struct regdef));
   412         if( block->numRegs >= regsAllocSize ) {
   413             regsAllocSize *= 2;
   414             block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
   415         }
   416         block->regs[block->numRegs++] = regdef;
   417         ioparse_regdef(regdef);
   419         tok = iolex(TOK_INTEGER);
   420     }
   422     qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb );
   424     return block;
   425 }
   427 GList *ioparse( const char *filename, GList *list )
   428 {
   429     GList *blocks = list;
   430     int count = 0;
   432     if( iolex_open(filename) != 0  )
   433         return blocks;
   435     int tok;
   436     while(1) {
   437         tok = iolex(TOK_REGISTERS);
   438         if( tok == TOK_EOF ) {
   439             int result = iolex_pop();
   440             if( result == -1 )
   441                 break;
   442         } else if( tok == TOK_INCLUDE) {
   443             READ(TOK_STRING);
   444             char *tmp = yystrdup();
   445             READ(TOK_SEMI);
   446             int result = iolex_push( tmp );
   447             if( result == -1 ) {
   448                 YYPARSE_ERROR("Unable to include file '%s'", tmp);
   449             }
   450             free(tmp);
   451         } else if( tok == TOK_SPACE ) {
   452         } else if( tok == TOK_REGISTERS ) {
   453             struct regblock *block = ioparse_regblock(block);
   454             count++;
   455             blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb);
   456         } else {
   457             YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] );
   458         }
   459     }
   460     return blocks;
   461 }
   463 /**************************** Lexical analyser ***************************/
   465 static int iolex_push( const char *filename )
   466 {
   467     struct yystate *save = g_malloc(sizeof(struct yystate));
   468     memcpy( save, &yystate, sizeof(struct yystate) );
   470     int result = iolex_open(filename);
   471     if( result == 0 ) {
   472         yyfile_stack = g_list_prepend(yyfile_stack, save);
   473     }
   474     return result;
   475 }
   477 static int iolex_pop( )
   478 {
   479     iolex_close();
   480     if( yyfile_stack == NULL )
   481         return -1;
   482     struct yystate *top = (struct yystate *)yyfile_stack->data;
   483     yyfile_stack = g_list_remove(yyfile_stack, top);
   484     memcpy( &yystate, top, sizeof(struct yystate) );
   485     g_free( top );
   486     return 0;
   487 }
   489 static int iolex_open( const char *filename )
   490 {
   491     struct stat st;
   492     int fd = open( filename, O_RDONLY );
   493     if( fd == -1 ) {
   494         fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) );
   495         return -1;
   496     }
   498     if( fstat( fd, &st ) != 0 ) {
   499         fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) );
   500         close(fd);
   501         return -1;
   502     }
   504     char *data = g_malloc( st.st_size + 1 );
   505     if( read(fd, data, st.st_size) != st.st_size ) {
   506         fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) );
   507         close(fd);
   508         return -1;
   509     }
   510     close(fd);
   511     data[st.st_size] = 0;
   513     yystate.yybuffer = yystate.yyposn = data;
   514     yystate.yyend = data + st.st_size;
   515     yystate.yylineno = 1;
   516     yystate.yylineposn = yystate.yyposn;
   517     yystate.yyfilename = strdup(filename);
   518     return 0;
   519 }
   521 static void iolex_close()
   522 {
   523     g_free(yystate.yybuffer);
   524     free(yystate.yyfilename);
   525     memset(&yystate, 0, sizeof(struct yystate));
   526 }
   528 #define YYRETURN(x) do{ \
   529     yytok.yylength = yystate.yyposn - yystart; \
   530     return (x); \
   531 } while(0)
   533 static int iolex_readhex(char *p, int digits)
   534 {
   535     int result = 0;
   536     for( int i=0; i < digits; i++ ) {
   537         result <<= 4;
   538         if( isdigit(p[i]) ) {
   539             result += p[i]-'0';
   540         } else if( p[i] >= 'a' && p[i] <= 'f' ) {
   541             result += p[i]-'a'+10;
   542         } else if( p[i] >= 'A' && p[i] <= 'F' ) {
   543             result += p[i]-'A'+10;
   544         } else {
   545             return (result >> 4);
   546         }
   548     }
   549     return result;
   550 }
   552 static int iolex_readoctal(char *p, int digits)
   553 {
   554     int result = 0;
   555     for( int i=0; i < digits; i++ ) {
   556         result <<= 3;
   557         if( p[i] >= '0' && p[i] <= '7' ) {
   558             result += p[i]-'0';
   559         } else {
   560             return (result >> 4);
   561         }
   562     }
   563     return result;
   564 }
   566 /**
   567  * Copy and interpret the string segment as a C string, replacing escape
   568  * expressions with the corresponding value.
   569  */
   570 static char *iolex_getcstring( char *start, char *end, int *len )
   571 {
   572    char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */
   573    char *q = result;
   575    for( char *p = start; p != end; p++ ) {
   576        if( *p == '\\' ) {
   577            if( ++p == end ) {
   578                *q++ = '\\';
   579                break;
   580            }
   581            if( p[0] >= '0' && p[0] <= '3' && p+3 <= end &&
   582                p[1] >= '0' && p[1] <= '7' &&
   583                p[2] >= '0' && p[2] <= '7' ) {
   584                *q++ = (char)iolex_readoctal(p,3);
   585                p+=2;
   586            } else {
   587                switch( *p ) {
   588                case 'n':
   589                    *q++ = '\n';
   590                    break;
   591                case 'r':
   592                    *q++ = '\r';
   593                    break;
   594                case 't':
   595                    *q++ = '\t';
   596                    break;
   597                case 'x': /* hex */
   598                    if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) {
   599                        *q++ = '\\';
   600                        *q++ = *p;
   601                    } else {
   602                        *q++ = (char)iolex_readhex(p+1, 2);
   603                        p+=2;
   604                    }
   605                    break;
   606                default:
   607                    *q++ = '\\';
   608                    *q++ = *p;
   609                }
   610            }
   611        } else {
   612            *q++ = *p;
   613        }
   614    }
   615    *len = q - result;
   616    return result;
   617 }
   619 int iolex( int expectToken )
   620 {
   621     int count = 0;
   622     while( yystate.yyposn < yystate.yyend ) {
   623         char *yystart = yytok.yytext = yystate.yyposn;
   624         yytok.yylength = 1;
   625         yytok.yyline = yystate.yylineno;
   626         yytok.yycol = yystate.yyposn - yystate.yylineposn+1;
   627         int ch = *yystate.yyposn++;
   628         if( isdigit(ch) ) {
   629             /* INTEGER */
   630             if( ch == '0' ) {
   631                 if( *yystate.yyposn == 'x' ) {
   632                     while( yystate.yyposn < yystate.yyend && isxdigit(*++yystate.yyposn) ) ;
   633                 } else {
   634                     while( yystate.yyposn < yystate.yyend && *yystate.yyposn >= '0' && *yystate.yyposn <= '7' )
   635                         yystate.yyposn++;
   636                 }
   637             } else {
   638                 while( yystate.yyposn < yystate.yyend && isdigit(*yystate.yyposn) )
   639                     yystate.yyposn++;
   640             }
   641             yytok.v.i = strtol( yystart, NULL, 0 );
   642             YYRETURN(TOK_INTEGER);
   643         } else if( isalpha(ch) || ch == '_' ) {
   644             /* IDENTIFIER */
   645             while( yystate.yyposn < yystate.yyend && (isalnum(*yystate.yyposn) || *yystate.yyposn == '_') )
   646                 yystate.yyposn++;
   647             yytok.yylength = yystate.yyposn - yystart;
   648             if( expectToken == TOK_IDENTIFIER ) {
   649                 YYRETURN(TOK_IDENTIFIER);
   650             }
   651             /* Otherwise check for keywords */
   652             for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) {
   653                 if( strlen(TOKEN_NAMES[i]) == yytok.yylength &&
   654                     strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) {
   655                     YYRETURN(i);
   656                 }
   657             }
   658             YYRETURN(TOK_IDENTIFIER);
   659         } else if( isspace(ch) ) {
   660             if( ch == '\n' ) {
   661                 yystate.yylineno++;
   662                 yystate.yylineposn = yystate.yyposn;
   663             }
   664             while( isspace(*yystate.yyposn) ) {
   665                 if( *yystate.yyposn == '\n' ) {
   666                     yystate.yylineno++;
   667                     yystate.yylineposn = yystate.yyposn+1;
   668                 }
   669                 yystate.yyposn++;
   670             }
   671         } else {
   672             switch( ch ) {
   673             case '(': YYRETURN(TOK_LPAREN);
   674             case ')': YYRETURN(TOK_RPAREN);
   675             case '[': YYRETURN(TOK_LSQUARE);
   676             case ']': YYRETURN(TOK_RSQUARE);
   677             case ',': YYRETURN(TOK_COMMA);
   678             case ':': YYRETURN(TOK_COLON);
   679             case ';': YYRETURN(TOK_SEMI);
   680             case '=': YYRETURN(TOK_EQUALS);
   681             case '/':
   682                 if( *yystate.yyposn == '/' ) { /* Line comment */
   683                     while( yystate.yyposn < yystate.yyend && *++yystate.yyposn != '\n' ) ;
   684                 } else if( *yystate.yyposn == '*' ) { /* C comment */
   685                     while( yystate.yyposn < yystate.yyend && (*++yystate.yyposn != '*' || *++yystate.yyposn != '/' ) ) {
   686                         if( *yystate.yyposn == '\n' ) {
   687                             yystate.yylineno++;
   688                             yystate.yylineposn = yystate.yyposn+1;
   689                         }
   690                     }
   691                 }
   692                 break;
   693             case '\'': /* STRING */
   694                 while( *yystate.yyposn != '\'' ) {
   695                     if( *yystate.yyposn == '\n' ) {
   696                         fprintf( stderr, "Unexpected newline in string constant!\n" );
   697                         YYRETURN(TOK_ERROR);
   698                     } else if( yystate.yyposn >= yystate.yyend ) {
   699                         fprintf( stderr, "Unexpected EOF in string constant!\n" );
   700                         YYRETURN(TOK_ERROR);
   701                     } else if( *yystate.yyposn == '\\' && yystate.yyposn[1] == '\'' ) {
   702                         yystate.yyposn++;
   703                     }
   704                     yystate.yyposn++;
   705                 }
   706                 yystate.yyposn++;
   707                 yytok.v.s = iolex_getcstring(yystart+1, yystate.yyposn-1, &yytok.slen);
   708                 YYRETURN(TOK_STRING);
   709             case '\"': /* STRING */
   710                 while( *yystate.yyposn != '\"' ) {
   711                     if( *yystate.yyposn == '\n' ) {
   712                         fprintf( stderr, "Unexpected newline in string constant!\n" );
   713                         YYRETURN(TOK_ERROR);
   714                     } else if( yystate.yyposn >= yystate.yyend ) {
   715                         fprintf( stderr, "Unexpected EOF in string constant!\n" );
   716                         YYRETURN(TOK_ERROR);
   717                     } else if( *yystate.yyposn == '\\' && yystate.yyposn[1] == '\"' ) {
   718                         yystate.yyposn++;
   719                     }
   720                     yystate.yyposn++;
   721                 }
   722                 yystate.yyposn++;
   723                 yytok.v.s = iolex_getcstring(yystart+1, yystate.yyposn-1, &yytok.slen);
   724                 YYRETURN(TOK_STRING);
   725             case '}':
   726                 YYRETURN(TOK_RBRACE);
   727             case '{': /* ACTION or LBRACE */
   728                 if( expectToken == TOK_LBRACE ) {
   729                     YYRETURN(TOK_LBRACE);
   730                 } else {
   731                     count++;
   732                     while( count > 0 && yystate.yyposn < yystate.yyend ) {
   733                         if( *yystate.yyposn == '{' )
   734                             count++;
   735                         if( *yystate.yyposn == '}' )
   736                             count--;
   737                         yystate.yyposn++;
   738                     }
   739                     YYRETURN(TOK_ACTION);
   740                 }
   741             case '.':
   742                 if( *yystate.yyposn == '.' ) {
   743                     yystate.yyposn++;
   744                     YYRETURN(TOK_RANGE);
   745                 } else {
   746                     YYRETURN(TOK_PERIOD);
   747                 }
   748             default:
   749                 fprintf( stderr, "Illegal character: '%c'\n", ch );
   750                 YYRETURN(TOK_ERROR);
   751             }
   752         }
   754     }
   755     return TOK_EOF;
   756 }
.