Search
lxdream.org :: lxdream/src/tools/mdparse.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/mdparse.c
changeset 1298:d0eb2307b847
prev1296:30ecee61f811
author nkeynes
date Wed Feb 04 08:38:23 2015 +1000 (5 years ago)
permissions -rw-r--r--
last change Fix assorted compile warnings reported by Clang
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@1296
    28
#include <glib.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@1298
   110
        uint64_t 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@1298
   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@1298
   162
static int yymatch( const char *arr[], unsigned numOptions, const char *desc )
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@1298
   169
    YYPARSE_ERROR( "Unknown %s '%s'", desc, yystrdup() );
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@1298
   193
            YYPARSE_ERROR( "Expected string initializer but was an integer (0x%llx)", yytok.v.i );
nkeynes@1104
   194
        }
nkeynes@1298
   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@1298
   227
            flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES), "access mode");
nkeynes@1104
   228
            break;
nkeynes@1104
   229
        case TOK_MASK:
nkeynes@1104
   230
            if( numBytes ) {
nkeynes@1104
   231
                READ(TOK_EQUALS);
nkeynes@1104
   232
                tok = iolex(TOK_INTEGER);
nkeynes@1104
   233
                ioparse_apval( tok, &flags->maskValue, numBytes );
nkeynes@1104
   234
            } else {
nkeynes@1298
   235
                YYPARSE_ERROR("mask is not valid on a register block");
nkeynes@1104
   236
            }
nkeynes@1104
   237
            break;
nkeynes@1104
   238
        case TOK_ENDIAN:
nkeynes@1104
   239
            READ(TOK_EQUALS);
nkeynes@1104
   240
            READ(TOK_IDENTIFIER);
nkeynes@1298
   241
            flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES), "endianness");
nkeynes@1104
   242
            break;
nkeynes@1104
   243
        case TOK_TRACE:
nkeynes@1104
   244
            READ(TOK_EQUALS);
nkeynes@1104
   245
            READ(TOK_IDENTIFIER);
nkeynes@1298
   246
            flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES), "trace flags");
nkeynes@1104
   247
            break;
nkeynes@1104
   248
        case TOK_TEST:
nkeynes@1104
   249
            READ(TOK_EQUALS);
nkeynes@1104
   250
            READ(TOK_IDENTIFIER);
nkeynes@1298
   251
            flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES), "test flag");
nkeynes@1104
   252
            break;
nkeynes@1104
   253
        case TOK_COMMA:
nkeynes@1104
   254
            break;
nkeynes@1104
   255
        case TOK_RPAREN:
nkeynes@1104
   256
            return;
nkeynes@1104
   257
        default:
nkeynes@1104
   258
            YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]);
nkeynes@1104
   259
        }
nkeynes@1104
   260
    } while(1);
nkeynes@1104
   261
}
nkeynes@1104
   262
nkeynes@1104
   263
static void ioparse_regdef( struct regdef *reg )
nkeynes@1104
   264
{
nkeynes@1298
   265
    reg->offset = (uint32_t)yytok.v.i;
nkeynes@1104
   266
    reg->numElements = 1;
nkeynes@1104
   267
    reg->numBytes = 0;
nkeynes@1104
   268
    reg->initValue.i = 0;
nkeynes@1104
   269
    reg->flags.maskValue.i = -1;
nkeynes@1298
   270
    uint32_t rangeOffset = reg->offset;
nkeynes@1104
   271
nkeynes@1104
   272
    int tok = iolex(TOK_COLON);
nkeynes@1104
   273
    if( tok == TOK_RANGE ) {
nkeynes@1104
   274
        READ(TOK_INTEGER);
nkeynes@1298
   275
        rangeOffset = (uint32_t)yytok.v.i;
nkeynes@1104
   276
        if( rangeOffset < reg->offset ) {
nkeynes@1104
   277
            YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset );
nkeynes@1104
   278
        }
nkeynes@1104
   279
        READ(TOK_COLON);
nkeynes@1104
   280
    } else if( tok != TOK_COLON ) {
nkeynes@1104
   281
        YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   282
    }
nkeynes@1104
   283
    READ(TOK_IDENTIFIER);
nkeynes@1298
   284
    reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES), "register mode");
nkeynes@1104
   285
    if( reg->mode == REG_MIRROR ) {
nkeynes@1104
   286
        /* Mirror regions have a target range only */
nkeynes@1104
   287
        READ(TOK_INTEGER);
nkeynes@1104
   288
        reg->initValue.i = yytok.v.i;
nkeynes@1104
   289
        reg->type = REG_I8;
nkeynes@1104
   290
        tok = iolex(TOK_RANGE);
nkeynes@1104
   291
        if( tok == TOK_RANGE ) {
nkeynes@1104
   292
            READ(TOK_INTEGER);
nkeynes@1104
   293
            if( yytok.v.i < reg->initValue.i ) {
nkeynes@1298
   294
                YYPARSE_ERROR( "Invalid mirror target range 0x%llx..0x%llx", reg->initValue.i, yytok.v.i );
nkeynes@1104
   295
            }
nkeynes@1298
   296
            reg->numBytes = (uint32_t)(yytok.v.i - reg->initValue.i + 1);
nkeynes@1104
   297
            tok = iolex(TOK_STRIDE);
nkeynes@1104
   298
        }
nkeynes@1104
   299
        if( tok == TOK_STRIDE ) {
nkeynes@1104
   300
            READ(TOK_INTEGER);
nkeynes@1298
   301
            reg->stride = (uint32_t)yytok.v.i;
nkeynes@1104
   302
            tok = iolex(TOK_ACTION);
nkeynes@1104
   303
        } else {
nkeynes@1104
   304
            reg->stride = reg->numBytes;
nkeynes@1104
   305
        }
nkeynes@1104
   306
nkeynes@1104
   307
        if( tok != TOK_SEMI ) {
nkeynes@1104
   308
            YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] );
nkeynes@1104
   309
        }
nkeynes@1104
   310
nkeynes@1104
   311
        if( reg->stride < reg->numBytes ) {
nkeynes@1104
   312
            YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes );
nkeynes@1104
   313
        }
nkeynes@1104
   314
nkeynes@1104
   315
        if( rangeOffset != reg->offset ) {
nkeynes@1104
   316
            reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride;
nkeynes@1104
   317
        }
nkeynes@1104
   318
nkeynes@1104
   319
    } else {
nkeynes@1104
   320
        READ(TOK_IDENTIFIER);
nkeynes@1298
   321
        reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES), "register type");
nkeynes@1104
   322
        reg->numBytes = TYPE_SIZES[reg->type];
nkeynes@1104
   323
        tok = iolex(TOK_IDENTIFIER);
nkeynes@1104
   324
        if( tok == TOK_IDENTIFIER ) {
nkeynes@1104
   325
            reg->name = yystrdup();
nkeynes@1104
   326
            tok = iolex(TOK_ACTION);
nkeynes@1104
   327
        }
nkeynes@1104
   328
        if( tok == TOK_STRING ) {
nkeynes@1104
   329
            reg->description = yytok.v.s;
nkeynes@1104
   330
            tok = iolex(TOK_ACTION);
nkeynes@1104
   331
        }
nkeynes@1104
   332
        if( tok == TOK_LPAREN ) {
nkeynes@1104
   333
            ioparse_regflags(&reg->flags, &reg->numBytes);
nkeynes@1104
   334
            tok = iolex(TOK_ACTION);
nkeynes@1104
   335
        }
nkeynes@1104
   336
        if( tok == TOK_EQUALS ) {
nkeynes@1104
   337
            tok = iolex(TOK_INTEGER);
nkeynes@1104
   338
            if( tok == TOK_UNDEFINED ) {
nkeynes@1104
   339
                reg->initValue.i = 0xDEADBEEFDEADBEEF;
nkeynes@1104
   340
                reg->initUndefined = TRUE;
nkeynes@1104
   341
            } else {
nkeynes@1104
   342
                ioparse_apval(tok, &reg->initValue, &reg->numBytes);
nkeynes@1104
   343
            }
nkeynes@1104
   344
            tok = iolex(TOK_ACTION);
nkeynes@1104
   345
        } else if( reg->type == REG_STRING ) {
nkeynes@1298
   346
            YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')" );
nkeynes@1104
   347
        }
nkeynes@1104
   348
        if( tok == TOK_ACTION ) {
nkeynes@1104
   349
            // reg->action = yystrdup();
nkeynes@1104
   350
        } else if( tok != TOK_SEMI ) {
nkeynes@1104
   351
            YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] );
nkeynes@1104
   352
        }
nkeynes@1104
   353
nkeynes@1104
   354
        if( rangeOffset != reg->offset ) {
nkeynes@1104
   355
            reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes;
nkeynes@1104
   356
        }
nkeynes@1104
   357
    }
nkeynes@1104
   358
nkeynes@1104
   359
}
nkeynes@1104
   360
nkeynes@1104
   361
static struct regblock *ioparse_regblock( )
nkeynes@1104
   362
{
nkeynes@1104
   363
    unsigned regsAllocSize = 128;
nkeynes@1104
   364
    struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
nkeynes@1104
   365
    block->numRegs = 0;
nkeynes@1104
   366
nkeynes@1104
   367
    READ(TOK_IDENTIFIER);
nkeynes@1104
   368
    block->name = yystrdup();
nkeynes@1104
   369
    int tok = iolex(TOK_AT);
nkeynes@1104
   370
    if( tok == TOK_STRING ) {
nkeynes@1104
   371
        block->description = yytok.v.s;
nkeynes@1104
   372
        tok = iolex(TOK_AT);
nkeynes@1104
   373
    }
nkeynes@1104
   374
    if( tok != TOK_AT ) {
nkeynes@1104
   375
        YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   376
    }
nkeynes@1104
   377
    READ(TOK_INTEGER);
nkeynes@1298
   378
    block->address = (uint32_t)yytok.v.i;
nkeynes@1104
   379
nkeynes@1104
   380
    tok = iolex(TOK_LBRACE);
nkeynes@1104
   381
    if( tok == TOK_LPAREN) {
nkeynes@1104
   382
        ioparse_regflags(&block->flags, NULL);
nkeynes@1104
   383
        READ(TOK_LBRACE);
nkeynes@1104
   384
    } else if( tok != TOK_LBRACE ) {
nkeynes@1104
   385
        YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   386
    }
nkeynes@1104
   387
nkeynes@1104
   388
    tok = iolex(TOK_INTEGER);
nkeynes@1104
   389
    while( tok != TOK_RBRACE ) {
nkeynes@1104
   390
        if( tok != TOK_INTEGER ) {
nkeynes@1104
   391
            YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]);
nkeynes@1104
   392
        }
nkeynes@1104
   393
        struct regdef *regdef = g_malloc0(sizeof(struct regdef));
nkeynes@1104
   394
        if( block->numRegs >= regsAllocSize ) {
nkeynes@1104
   395
            regsAllocSize *= 2;
nkeynes@1104
   396
            block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
nkeynes@1104
   397
        }
nkeynes@1104
   398
        block->regs[block->numRegs++] = regdef;
nkeynes@1104
   399
        ioparse_regdef(regdef);
nkeynes@1104
   400
nkeynes@1104
   401
        tok = iolex(TOK_INTEGER);
nkeynes@1104
   402
    }
nkeynes@1104
   403
nkeynes@1104
   404
    qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb );
nkeynes@1104
   405
nkeynes@1104
   406
    return block;
nkeynes@1104
   407
}
nkeynes@1104
   408
nkeynes@1104
   409
GList *ioparse( const char *filename, GList *list )
nkeynes@1104
   410
{
nkeynes@1104
   411
    GList *blocks = list;
nkeynes@1104
   412
    int count = 0;
nkeynes@1104
   413
nkeynes@1104
   414
    if( iolex_open(filename) != 0  )
nkeynes@1104
   415
        return blocks;
nkeynes@1104
   416
nkeynes@1104
   417
    int tok;
nkeynes@1105
   418
    while(1) {
nkeynes@1104
   419
        tok = iolex(TOK_REGISTERS);
nkeynes@1104
   420
        if( tok == TOK_EOF ) {
nkeynes@1105
   421
            int result = iolex_pop();
nkeynes@1105
   422
            if( result == -1 )
nkeynes@1105
   423
                break;
nkeynes@1104
   424
        } else if( tok == TOK_INCLUDE) {
nkeynes@1104
   425
            READ(TOK_STRING);
nkeynes@1105
   426
            char *tmp = yystrdup();
nkeynes@1105
   427
            READ(TOK_SEMI);
nkeynes@1105
   428
            int result = iolex_push( tmp );
nkeynes@1105
   429
            if( result == -1 ) {
nkeynes@1105
   430
                YYPARSE_ERROR("Unable to include file '%s'", tmp);
nkeynes@1105
   431
            }
nkeynes@1105
   432
            free(tmp);
nkeynes@1104
   433
        } else if( tok == TOK_SPACE ) {
nkeynes@1104
   434
        } else if( tok == TOK_REGISTERS ) {
nkeynes@1298
   435
            struct regblock *block = ioparse_regblock();
nkeynes@1104
   436
            count++;
nkeynes@1104
   437
            blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb);
nkeynes@1104
   438
        } else {
nkeynes@1104
   439
            YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] );
nkeynes@1104
   440
        }
nkeynes@1104
   441
    }
nkeynes@1104
   442
    return blocks;
nkeynes@1104
   443
}
nkeynes@1104
   444
nkeynes@1104
   445
/**************************** Lexical analyser ***************************/
nkeynes@1104
   446
nkeynes@1105
   447
static int iolex_push( const char *filename )
nkeynes@1105
   448
{
nkeynes@1105
   449
    struct yystate *save = g_malloc(sizeof(struct yystate));
nkeynes@1105
   450
    memcpy( save, &yystate, sizeof(struct yystate) );
nkeynes@1105
   451
nkeynes@1105
   452
    int result = iolex_open(filename);
nkeynes@1105
   453
    if( result == 0 ) {
nkeynes@1105
   454
        yyfile_stack = g_list_prepend(yyfile_stack, save);
nkeynes@1105
   455
    }
nkeynes@1105
   456
    return result;
nkeynes@1105
   457
}
nkeynes@1105
   458
nkeynes@1105
   459
static int iolex_pop( )
nkeynes@1105
   460
{
nkeynes@1105
   461
    iolex_close();
nkeynes@1105
   462
    if( yyfile_stack == NULL )
nkeynes@1105
   463
        return -1;
nkeynes@1105
   464
    struct yystate *top = (struct yystate *)yyfile_stack->data;
nkeynes@1105
   465
    yyfile_stack = g_list_remove(yyfile_stack, top);
nkeynes@1105
   466
    memcpy( &yystate, top, sizeof(struct yystate) );
nkeynes@1105
   467
    g_free( top );
nkeynes@1105
   468
    return 0;
nkeynes@1105
   469
}
nkeynes@1105
   470
nkeynes@1104
   471
static int iolex_open( const char *filename )
nkeynes@1104
   472
{
nkeynes@1104
   473
    struct stat st;
nkeynes@1104
   474
    int fd = open( filename, O_RDONLY );
nkeynes@1104
   475
    if( fd == -1 ) {
nkeynes@1104
   476
        fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) );
nkeynes@1104
   477
        return -1;
nkeynes@1104
   478
    }
nkeynes@1104
   479
nkeynes@1104
   480
    if( fstat( fd, &st ) != 0 ) {
nkeynes@1104
   481
        fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) );
nkeynes@1104
   482
        close(fd);
nkeynes@1104
   483
        return -1;
nkeynes@1104
   484
    }
nkeynes@1104
   485
nkeynes@1104
   486
    char *data = g_malloc( st.st_size + 1 );
nkeynes@1104
   487
    if( read(fd, data, st.st_size) != st.st_size ) {
nkeynes@1104
   488
        fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) );
nkeynes@1104
   489
        close(fd);
nkeynes@1104
   490
        return -1;
nkeynes@1104
   491
    }
nkeynes@1104
   492
    close(fd);
nkeynes@1104
   493
    data[st.st_size] = 0;
nkeynes@1104
   494
nkeynes@1105
   495
    yystate.yybuffer = yystate.yyposn = data;
nkeynes@1105
   496
    yystate.yyend = data + st.st_size;
nkeynes@1105
   497
    yystate.yylineno = 1;
nkeynes@1105
   498
    yystate.yylineposn = yystate.yyposn;
nkeynes@1105
   499
    yystate.yyfilename = strdup(filename);
nkeynes@1104
   500
    return 0;
nkeynes@1104
   501
}
nkeynes@1104
   502
nkeynes@1104
   503
static void iolex_close()
nkeynes@1104
   504
{
nkeynes@1105
   505
    g_free(yystate.yybuffer);
nkeynes@1105
   506
    free(yystate.yyfilename);
nkeynes@1105
   507
    memset(&yystate, 0, sizeof(struct yystate));
nkeynes@1104
   508
}
nkeynes@1104
   509
nkeynes@1104
   510
#define YYRETURN(x) do{ \
nkeynes@1105
   511
    yytok.yylength = yystate.yyposn - yystart; \
nkeynes@1104
   512
    return (x); \
nkeynes@1104
   513
} while(0)
nkeynes@1104
   514
nkeynes@1104
   515
static int iolex_readhex(char *p, int digits)
nkeynes@1104
   516
{
nkeynes@1104
   517
    int result = 0;
nkeynes@1104
   518
    for( int i=0; i < digits; i++ ) {
nkeynes@1104
   519
        result <<= 4;
nkeynes@1104
   520
        if( isdigit(p[i]) ) {
nkeynes@1104
   521
            result += p[i]-'0';
nkeynes@1104
   522
        } else if( p[i] >= 'a' && p[i] <= 'f' ) {
nkeynes@1104
   523
            result += p[i]-'a'+10;
nkeynes@1104
   524
        } else if( p[i] >= 'A' && p[i] <= 'F' ) {
nkeynes@1104
   525
            result += p[i]-'A'+10;
nkeynes@1104
   526
        } else {
nkeynes@1104
   527
            return (result >> 4);
nkeynes@1104
   528
        }
nkeynes@1104
   529
nkeynes@1104
   530
    }
nkeynes@1104
   531
    return result;
nkeynes@1104
   532
}
nkeynes@1104
   533
nkeynes@1104
   534
static int iolex_readoctal(char *p, int digits)
nkeynes@1104
   535
{
nkeynes@1104
   536
    int result = 0;
nkeynes@1104
   537
    for( int i=0; i < digits; i++ ) {
nkeynes@1104
   538
        result <<= 3;
nkeynes@1104
   539
        if( p[i] >= '0' && p[i] <= '7' ) {
nkeynes@1104
   540
            result += p[i]-'0';
nkeynes@1104
   541
        } else {
nkeynes@1104
   542
            return (result >> 4);
nkeynes@1104
   543
        }
nkeynes@1104
   544
    }
nkeynes@1104
   545
    return result;
nkeynes@1104
   546
}
nkeynes@1104
   547
nkeynes@1104
   548
/**
nkeynes@1104
   549
 * Copy and interpret the string segment as a C string, replacing escape
nkeynes@1104
   550
 * expressions with the corresponding value.
nkeynes@1104
   551
 */
nkeynes@1104
   552
static char *iolex_getcstring( char *start, char *end, int *len )
nkeynes@1104
   553
{
nkeynes@1104
   554
   char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */
nkeynes@1104
   555
   char *q = result;
nkeynes@1104
   556
nkeynes@1104
   557
   for( char *p = start; p != end; p++ ) {
nkeynes@1104
   558
       if( *p == '\\' ) {
nkeynes@1104
   559
           if( ++p == end ) {
nkeynes@1104
   560
               *q++ = '\\';
nkeynes@1104
   561
               break;
nkeynes@1104
   562
           }
nkeynes@1104
   563
           if( p[0] >= '0' && p[0] <= '3' && p+3 <= end &&
nkeynes@1104
   564
               p[1] >= '0' && p[1] <= '7' &&
nkeynes@1104
   565
               p[2] >= '0' && p[2] <= '7' ) {
nkeynes@1104
   566
               *q++ = (char)iolex_readoctal(p,3);
nkeynes@1104
   567
               p+=2;
nkeynes@1104
   568
           } else {
nkeynes@1104
   569
               switch( *p ) {
nkeynes@1104
   570
               case 'n':
nkeynes@1104
   571
                   *q++ = '\n';
nkeynes@1104
   572
                   break;
nkeynes@1104
   573
               case 'r':
nkeynes@1104
   574
                   *q++ = '\r';
nkeynes@1104
   575
                   break;
nkeynes@1104
   576
               case 't':
nkeynes@1104
   577
                   *q++ = '\t';
nkeynes@1104
   578
                   break;
nkeynes@1104
   579
               case 'x': /* hex */
nkeynes@1104
   580
                   if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) {
nkeynes@1104
   581
                       *q++ = '\\';
nkeynes@1104
   582
                       *q++ = *p;
nkeynes@1104
   583
                   } else {
nkeynes@1104
   584
                       *q++ = (char)iolex_readhex(p+1, 2);
nkeynes@1104
   585
                       p+=2;
nkeynes@1104
   586
                   }
nkeynes@1104
   587
                   break;
nkeynes@1104
   588
               default:
nkeynes@1104
   589
                   *q++ = '\\';
nkeynes@1104
   590
                   *q++ = *p;
nkeynes@1104
   591
               }
nkeynes@1104
   592
           }
nkeynes@1104
   593
       } else {
nkeynes@1104
   594
           *q++ = *p;
nkeynes@1104
   595
       }
nkeynes@1104
   596
   }
nkeynes@1104
   597
   *len = q - result;
nkeynes@1104
   598
   return result;
nkeynes@1104
   599
}
nkeynes@1104
   600
nkeynes@1104
   601
int iolex( int expectToken )
nkeynes@1104
   602
{
nkeynes@1104
   603
    int count = 0;
nkeynes@1105
   604
    while( yystate.yyposn < yystate.yyend ) {
nkeynes@1105
   605
        char *yystart = yytok.yytext = yystate.yyposn;
nkeynes@1104
   606
        yytok.yylength = 1;
nkeynes@1105
   607
        yytok.yyline = yystate.yylineno;
nkeynes@1105
   608
        yytok.yycol = yystate.yyposn - yystate.yylineposn+1;
nkeynes@1105
   609
        int ch = *yystate.yyposn++;
nkeynes@1104
   610
        if( isdigit(ch) ) {
nkeynes@1104
   611
            /* INTEGER */
nkeynes@1104
   612
            if( ch == '0' ) {
nkeynes@1105
   613
                if( *yystate.yyposn == 'x' ) {
nkeynes@1105
   614
                    while( yystate.yyposn < yystate.yyend && isxdigit(*++yystate.yyposn) ) ;
nkeynes@1104
   615
                } else {
nkeynes@1105
   616
                    while( yystate.yyposn < yystate.yyend && *yystate.yyposn >= '0' && *yystate.yyposn <= '7' )
nkeynes@1105
   617
                        yystate.yyposn++;
nkeynes@1104
   618
                }
nkeynes@1104
   619
            } else {
nkeynes@1105
   620
                while( yystate.yyposn < yystate.yyend && isdigit(*yystate.yyposn) )
nkeynes@1105
   621
                    yystate.yyposn++;
nkeynes@1104
   622
            }
nkeynes@1298
   623
            yytok.v.i = strtoll( yystart, NULL, 0 );
nkeynes@1104
   624
            YYRETURN(TOK_INTEGER);
nkeynes@1104
   625
        } else if( isalpha(ch) || ch == '_' ) {
nkeynes@1104
   626
            /* IDENTIFIER */
nkeynes@1105
   627
            while( yystate.yyposn < yystate.yyend && (isalnum(*yystate.yyposn) || *yystate.yyposn == '_') )
nkeynes@1105
   628
                yystate.yyposn++;
nkeynes@1105
   629
            yytok.yylength = yystate.yyposn - yystart;
nkeynes@1104
   630
            if( expectToken == TOK_IDENTIFIER ) {
nkeynes@1104
   631
                YYRETURN(TOK_IDENTIFIER);
nkeynes@1104
   632
            }
nkeynes@1104
   633
            /* Otherwise check for keywords */
nkeynes@1104
   634
            for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) {
nkeynes@1104
   635
                if( strlen(TOKEN_NAMES[i]) == yytok.yylength &&
nkeynes@1104
   636
                    strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) {
nkeynes@1104
   637
                    YYRETURN(i);
nkeynes@1104
   638
                }
nkeynes@1104
   639
            }
nkeynes@1104
   640
            YYRETURN(TOK_IDENTIFIER);
nkeynes@1104
   641
        } else if( isspace(ch) ) {
nkeynes@1104
   642
            if( ch == '\n' ) {
nkeynes@1105
   643
                yystate.yylineno++;
nkeynes@1105
   644
                yystate.yylineposn = yystate.yyposn;
nkeynes@1104
   645
            }
nkeynes@1105
   646
            while( isspace(*yystate.yyposn) ) {
nkeynes@1105
   647
                if( *yystate.yyposn == '\n' ) {
nkeynes@1105
   648
                    yystate.yylineno++;
nkeynes@1105
   649
                    yystate.yylineposn = yystate.yyposn+1;
nkeynes@1104
   650
                }
nkeynes@1105
   651
                yystate.yyposn++;
nkeynes@1104
   652
            }
nkeynes@1104
   653
        } else {
nkeynes@1104
   654
            switch( ch ) {
nkeynes@1104
   655
            case '(': YYRETURN(TOK_LPAREN);
nkeynes@1104
   656
            case ')': YYRETURN(TOK_RPAREN);
nkeynes@1104
   657
            case '[': YYRETURN(TOK_LSQUARE);
nkeynes@1104
   658
            case ']': YYRETURN(TOK_RSQUARE);
nkeynes@1104
   659
            case ',': YYRETURN(TOK_COMMA);
nkeynes@1104
   660
            case ':': YYRETURN(TOK_COLON);
nkeynes@1104
   661
            case ';': YYRETURN(TOK_SEMI);
nkeynes@1104
   662
            case '=': YYRETURN(TOK_EQUALS);
nkeynes@1104
   663
            case '/':
nkeynes@1105
   664
                if( *yystate.yyposn == '/' ) { /* Line comment */
nkeynes@1105
   665
                    while( yystate.yyposn < yystate.yyend && *++yystate.yyposn != '\n' ) ;
nkeynes@1105
   666
                } else if( *yystate.yyposn == '*' ) { /* C comment */
nkeynes@1105
   667
                    while( yystate.yyposn < yystate.yyend && (*++yystate.yyposn != '*' || *++yystate.yyposn != '/' ) ) {
nkeynes@1105
   668
                        if( *yystate.yyposn == '\n' ) {
nkeynes@1105
   669
                            yystate.yylineno++;
nkeynes@1105
   670
                            yystate.yylineposn = yystate.yyposn+1;
nkeynes@1104
   671
                        }
nkeynes@1104
   672
                    }
nkeynes@1104
   673
                }
nkeynes@1104
   674
                break;
nkeynes@1104
   675
            case '\'': /* STRING */
nkeynes@1105
   676
                while( *yystate.yyposn != '\'' ) {
nkeynes@1105
   677
                    if( *yystate.yyposn == '\n' ) {
nkeynes@1104
   678
                        fprintf( stderr, "Unexpected newline in string constant!\n" );
nkeynes@1104
   679
                        YYRETURN(TOK_ERROR);
nkeynes@1105
   680
                    } else if( yystate.yyposn >= yystate.yyend ) {
nkeynes@1104
   681
                        fprintf( stderr, "Unexpected EOF in string constant!\n" );
nkeynes@1104
   682
                        YYRETURN(TOK_ERROR);
nkeynes@1105
   683
                    } else if( *yystate.yyposn == '\\' && yystate.yyposn[1] == '\'' ) {
nkeynes@1105
   684
                        yystate.yyposn++;
nkeynes@1104
   685
                    }
nkeynes@1105
   686
                    yystate.yyposn++;
nkeynes@1104
   687
                }
nkeynes@1105
   688
                yystate.yyposn++;
nkeynes@1105
   689
                yytok.v.s = iolex_getcstring(yystart+1, yystate.yyposn-1, &yytok.slen);
nkeynes@1104
   690
                YYRETURN(TOK_STRING);
nkeynes@1104
   691
            case '\"': /* STRING */
nkeynes@1105
   692
                while( *yystate.yyposn != '\"' ) {
nkeynes@1105
   693
                    if( *yystate.yyposn == '\n' ) {
nkeynes@1104
   694
                        fprintf( stderr, "Unexpected newline in string constant!\n" );
nkeynes@1104
   695
                        YYRETURN(TOK_ERROR);
nkeynes@1105
   696
                    } else if( yystate.yyposn >= yystate.yyend ) {
nkeynes@1104
   697
                        fprintf( stderr, "Unexpected EOF in string constant!\n" );
nkeynes@1104
   698
                        YYRETURN(TOK_ERROR);
nkeynes@1105
   699
                    } else if( *yystate.yyposn == '\\' && yystate.yyposn[1] == '\"' ) {
nkeynes@1105
   700
                        yystate.yyposn++;
nkeynes@1104
   701
                    }
nkeynes@1105
   702
                    yystate.yyposn++;
nkeynes@1104
   703
                }
nkeynes@1105
   704
                yystate.yyposn++;
nkeynes@1105
   705
                yytok.v.s = iolex_getcstring(yystart+1, yystate.yyposn-1, &yytok.slen);
nkeynes@1104
   706
                YYRETURN(TOK_STRING);
nkeynes@1104
   707
            case '}':
nkeynes@1104
   708
                YYRETURN(TOK_RBRACE);
nkeynes@1104
   709
            case '{': /* ACTION or LBRACE */
nkeynes@1104
   710
                if( expectToken == TOK_LBRACE ) {
nkeynes@1104
   711
                    YYRETURN(TOK_LBRACE);
nkeynes@1104
   712
                } else {
nkeynes@1104
   713
                    count++;
nkeynes@1105
   714
                    while( count > 0 && yystate.yyposn < yystate.yyend ) {
nkeynes@1105
   715
                        if( *yystate.yyposn == '{' )
nkeynes@1104
   716
                            count++;
nkeynes@1105
   717
                        if( *yystate.yyposn == '}' )
nkeynes@1104
   718
                            count--;
nkeynes@1105
   719
                        yystate.yyposn++;
nkeynes@1104
   720
                    }
nkeynes@1104
   721
                    YYRETURN(TOK_ACTION);
nkeynes@1104
   722
                }
nkeynes@1104
   723
            case '.':
nkeynes@1105
   724
                if( *yystate.yyposn == '.' ) {
nkeynes@1105
   725
                    yystate.yyposn++;
nkeynes@1104
   726
                    YYRETURN(TOK_RANGE);
nkeynes@1104
   727
                } else {
nkeynes@1104
   728
                    YYRETURN(TOK_PERIOD);
nkeynes@1104
   729
                }
nkeynes@1104
   730
            default:
nkeynes@1104
   731
                fprintf( stderr, "Illegal character: '%c'\n", ch );
nkeynes@1104
   732
                YYRETURN(TOK_ERROR);
nkeynes@1104
   733
            }
nkeynes@1104
   734
        }
nkeynes@1104
   735
nkeynes@1104
   736
    }
nkeynes@1104
   737
    return TOK_EOF;
nkeynes@1104
   738
}
.