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