Search
lxdream.org :: lxdream/src/tools/mdparse.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/mdparse.c
changeset 1104:700e16c321e5
next1105:73da8fd129fb
author nkeynes
date Mon Mar 15 22:10:24 2010 +1000 (12 years ago)
permissions -rw-r--r--
last change Commit genmach work-in-progress
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/tools/mdparse.c Mon Mar 15 22:10:24 2010 +1000
1.3 @@ -0,0 +1,721 @@
1.4 +/**
1.5 + * $Id$
1.6 + *
1.7 + * genmmio I/O register definition parser.
1.8 + *
1.9 + * Copyright (c) 2010 Nathan Keynes.
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.14 + * (at your option) any later version.
1.15 + *
1.16 + * This program is distributed in the hope that it will be useful,
1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.20 + */
1.21 +
1.22 +#include <assert.h>
1.23 +#include <stdlib.h>
1.24 +#include <stdio.h>
1.25 +#include <string.h>
1.26 +#include <unistd.h>
1.27 +#include <fcntl.h>
1.28 +#include <ctype.h>
1.29 +#include <errno.h>
1.30 +#include <sys/stat.h>
1.31 +#include <glib/gmem.h>
1.32 +
1.33 +#include "genmach.h"
1.34 +/*
1.35 + * Grammar:
1.36 + *
1.37 + * start: start register_block | ;
1.38 + * register_block: 'registers' IDENTIFIER 'at' INTEGER opt_paramlist '{' register_list '}';
1.39 + * opt_paramlist: '(' param_list ')' | '(' ')' | ;
1.40 + * param_list: param_list ',' param | param ;
1.41 + * param: access_param | fill_param | endian_param | mask_param ;
1.42 + * access_param: 'access' '=' 'any' // Behaves as if it were memory
1.43 + * | 'access' '=' 'nooffset' // Recognize any size access (zero-ext or trunc) as long as the address is exactly the same
1.44 + * | 'access' '=' 'exact' // Recognize only the exact access type at the exact address
1.45 + * ;
1.46 + * fill_param: 'fill' '=' literal;
1.47 + * endian_param: 'endian' '=' 'little'
1.48 + * | 'endian' '=' 'big'
1.49 + * ;
1.50 + * mask_param: 'mask' '=' literal;
1.51 + *
1.52 + * register_list: register_list register_desc | ;
1.53 + * register_desc: INTEGER ':' mode_spec type_spec IDENTIFIER opt_paramlist [ '=' literal ] [ ACTION ]
1.54 + * mode_spec: 'const' | 'rw' | 'ro' | 'wo';
1.55 + * type_spec: 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64' | 'string' | 'cstring';
1.56 + * literal: INTEGER | STRING | 'undefined' ;
1.57 + *
1.58 + * C-style comments are recognized as such.
1.59 + */
1.60 +
1.61 +#define TOK_IDENTIFIER 1
1.62 +#define TOK_INTEGER 2
1.63 +#define TOK_STRING 3
1.64 +#define TOK_ACTION 4
1.65 +#define TOK_EOF 5
1.66 +#define TOK_ERROR 6
1.67 +#define TOK_LPAREN 7
1.68 +#define TOK_RPAREN 8
1.69 +#define TOK_LBRACE 9
1.70 +#define TOK_RBRACE 10
1.71 +#define TOK_COMMA 11
1.72 +#define TOK_COLON 12
1.73 +#define TOK_SEMI 13
1.74 +#define TOK_PERIOD 14
1.75 +#define TOK_RANGE 15
1.76 +#define TOK_LSQUARE 16
1.77 +#define TOK_RSQUARE 17
1.78 +#define TOK_EQUALS 18
1.79 +#define TOK_ACCESS 19
1.80 +#define TOK_AT 20
1.81 +#define TOK_ENDIAN 21
1.82 +#define TOK_FILL 22
1.83 +#define TOK_INCLUDE 23
1.84 +#define TOK_MASK 24
1.85 +#define TOK_REGISTERS 25
1.86 +#define TOK_SPACE 26
1.87 +#define TOK_STRIDE 27
1.88 +#define TOK_TEST 28
1.89 +#define TOK_TRACE 29
1.90 +#define TOK_UNDEFINED 30
1.91 +
1.92 +
1.93 +#define FIRST_KEYWORD TOK_ACCESS
1.94 +#define LAST_KEYWORD TOK_UNDEFINED
1.95 +static const char *TOKEN_NAMES[] = {"NULL", "IDENTIFIER", "INTEGER", "STRING", "ACTION", "EOF", "ERROR",
1.96 + "(", ")", "{", "}", ",", ":", ";", ".", "..", "[", "]", "=",
1.97 + "ACCESS", "AT", "ENDIAN", "FILL", "INCLUDE", "MASK", "REGISTERS", "SPACE", "STRIDE", "TEST", "TRACE", "UNDEFINED" };
1.98 +static const char *MODE_NAMES[] = {"const", "rw", "ro", "wo", "mirror"};
1.99 +static const char *TYPE_NAMES[] = {"i8", "i16", "i32", "i64", "f32", "f64", "string"};
1.100 +static int TYPE_SIZES[] = {1,2,4,8,4,8,0};
1.101 +static const char *ENDIAN_NAMES[] = {"default", "little", "big"};
1.102 +static const char *ACCESS_NAMES[] = {"default", "any", "nooffset", "exact"};
1.103 +static const char *TRACE_NAMES[] = {"default", "off", "on"};
1.104 +
1.105 +#define elementsof(x) (sizeof(x)/sizeof(x[0]))
1.106 +
1.107 +typedef struct token_data {
1.108 + char *yytext;
1.109 + int yylength;
1.110 + int yyline;
1.111 + int yycol;
1.112 + union {
1.113 + long i;
1.114 + char *s;
1.115 + } v;
1.116 + int slen;
1.117 +} token_data;
1.118 +
1.119 +static char *yybuffer;
1.120 +static char *yyposn, *yylineposn;
1.121 +static char *yyend;
1.122 +static int yyline;
1.123 +static struct token_data yytok;
1.124 +
1.125 +#define YYPARSE_ERROR( msg, ... ) \
1.126 + do { \
1.127 + fprintf( stderr, "Parse error at %d:%d: " msg "\n", yytok.yyline, yytok.yycol, __VA_ARGS__ ); \
1.128 + exit(2); \
1.129 + } while(0)
1.130 +
1.131 +#define READ(x) \
1.132 +{ int _tok = iolex(x); \
1.133 + if( _tok != (x) ) { \
1.134 + YYPARSE_ERROR( "Expected %s but got %s", TOKEN_NAMES[x], TOKEN_NAMES[_tok] ); \
1.135 + } \
1.136 +}
1.137 +
1.138 +static int iolex( int expectToken );
1.139 +static int iolex_open( const char *filename );
1.140 +static void iolex_close();
1.141 +
1.142 +static inline char *yystrdup()
1.143 +{
1.144 + char *str = g_malloc0(yytok.yylength+1);
1.145 + memcpy( str, yytok.yytext, yytok.yylength);
1.146 + return str;
1.147 +}
1.148 +
1.149 +static inline int yystrcasecmp(const char *cmp)
1.150 +{
1.151 + int len = strlen(cmp);
1.152 + if( len != yytok.yylength ) {
1.153 + return yytok.yylength - len;
1.154 + }
1.155 + return strncasecmp(yytok.yytext, cmp, yytok.yylength);
1.156 +}
1.157 +
1.158 +static int yymatch( const char *arr[], unsigned numOptions )
1.159 +{
1.160 + for( unsigned i=0; i<numOptions; i++ ) {
1.161 + if( yystrcasecmp( arr[i] ) == 0 ) {
1.162 + return i;
1.163 + }
1.164 + }
1.165 + return -1;
1.166 +}
1.167 +
1.168 +static gint register_block_sort_cb( gconstpointer a, gconstpointer b )
1.169 +{
1.170 + struct regblock *blocka = (struct regblock *)a;
1.171 + struct regblock *blockb = (struct regblock *)b;
1.172 + return blocka->address - blockb->address;
1.173 +}
1.174 +
1.175 +static int register_ptr_sort_cb( const void *a, const void *b )
1.176 +{
1.177 + regdef_t *ptra = (regdef_t *)a;
1.178 + regdef_t *ptrb = (regdef_t *)b;
1.179 + if( (*ptra)->offset != (*ptrb)->offset )
1.180 + return (*ptra)->offset - (*ptrb)->offset;
1.181 + return (*ptra)->type - (*ptrb)->type;
1.182 +}
1.183 +
1.184 +static void ioparse_apval(int tok, union apval *apv, unsigned *numBytes)
1.185 +{
1.186 + if( tok == TOK_INTEGER ) {
1.187 + apv->i = yytok.v.i;
1.188 + if( *numBytes == 0 ) {
1.189 + YYPARSE_ERROR( "Expected string initializer but was an integer (0x%x)", yytok.v.i );
1.190 + }
1.191 + } else if( tok = TOK_STRING ) {
1.192 + if( *numBytes == 0 ) {
1.193 + *numBytes = yytok.slen;
1.194 + apv->s = yytok.v.s;
1.195 + } else {
1.196 + if( *numBytes != yytok.slen ) {
1.197 + YYPARSE_ERROR( "Expected %d byte initializer but was %d", *numBytes, yytok.slen );
1.198 + }
1.199 + assert( *numBytes < sizeof(uint64_t) );
1.200 + apv->i = 0;
1.201 + /* FIXME: handle endian mismatches */
1.202 + memcpy( &apv->i, yytok.v.s, yytok.slen );
1.203 + }
1.204 + } else {
1.205 + YYPARSE_ERROR( "Expected literal (integer or string), but got %s", TOKEN_NAMES[tok] );
1.206 + }
1.207 +}
1.208 +
1.209 +static void ioparse_regflags( regflags_t flags, unsigned *numBytes )
1.210 +{
1.211 + do {
1.212 + int tok = iolex(TOK_RPAREN);
1.213 + switch(tok) {
1.214 + case TOK_FILL:
1.215 + READ(TOK_EQUALS);
1.216 + tok = iolex(TOK_INTEGER);
1.217 + flags->fillSizeBytes = 4;
1.218 + ioparse_apval( tok, &flags->fillValue, &flags->fillSizeBytes );
1.219 + break;
1.220 + case TOK_ACCESS:
1.221 + READ(TOK_EQUALS);
1.222 + READ(TOK_IDENTIFIER);
1.223 + flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES));
1.224 + if( flags->access == -1 ) {
1.225 + YYPARSE_ERROR("Unknown access mode '%s'", yystrdup());
1.226 + }
1.227 + break;
1.228 + case TOK_MASK:
1.229 + if( numBytes ) {
1.230 + READ(TOK_EQUALS);
1.231 + tok = iolex(TOK_INTEGER);
1.232 + ioparse_apval( tok, &flags->maskValue, numBytes );
1.233 + } else {
1.234 + YYPARSE_ERROR("mask is not valid on a register block",0);
1.235 + }
1.236 + break;
1.237 + case TOK_ENDIAN:
1.238 + READ(TOK_EQUALS);
1.239 + READ(TOK_IDENTIFIER);
1.240 + flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES));
1.241 + if( flags->endian == -1 ) {
1.242 + YYPARSE_ERROR("Unknown endianness '%s'", yystrdup());
1.243 + }
1.244 + break;
1.245 + case TOK_TRACE:
1.246 + READ(TOK_EQUALS);
1.247 + READ(TOK_IDENTIFIER);
1.248 + flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
1.249 + if( flags->traceFlag == -1 ) {
1.250 + YYPARSE_ERROR("Unknown trace flag '%s'", yystrdup());
1.251 + }
1.252 + break;
1.253 + case TOK_TEST:
1.254 + READ(TOK_EQUALS);
1.255 + READ(TOK_IDENTIFIER);
1.256 + flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
1.257 + if( flags->testFlag == -1 ) {
1.258 + YYPARSE_ERROR("Unknown test flag '%s'", yystrdup());
1.259 + }
1.260 + break;
1.261 + case TOK_COMMA:
1.262 + break;
1.263 + case TOK_RPAREN:
1.264 + return;
1.265 + default:
1.266 + YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]);
1.267 + }
1.268 + } while(1);
1.269 +}
1.270 +
1.271 +static void ioparse_regdef( struct regdef *reg )
1.272 +{
1.273 + reg->offset = yytok.v.i;
1.274 + reg->numElements = 1;
1.275 + reg->numBytes = 0;
1.276 + reg->initValue.i = 0;
1.277 + reg->flags.maskValue.i = -1;
1.278 + unsigned rangeOffset = reg->offset;
1.279 +
1.280 + int tok = iolex(TOK_COLON);
1.281 + if( tok == TOK_RANGE ) {
1.282 + READ(TOK_INTEGER);
1.283 + rangeOffset = yytok.v.i;
1.284 + if( rangeOffset < reg->offset ) {
1.285 + YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset );
1.286 + }
1.287 + READ(TOK_COLON);
1.288 + } else if( tok != TOK_COLON ) {
1.289 + YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] );
1.290 + }
1.291 + READ(TOK_IDENTIFIER);
1.292 + reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES));
1.293 + if( reg->mode == -1 ) {
1.294 + YYPARSE_ERROR( "Unknown register mode '%s'", yystrdup() );
1.295 + }
1.296 + if( reg->mode == REG_MIRROR ) {
1.297 + /* Mirror regions have a target range only */
1.298 + READ(TOK_INTEGER);
1.299 + reg->initValue.i = yytok.v.i;
1.300 + reg->type = REG_I8;
1.301 + tok = iolex(TOK_RANGE);
1.302 + if( tok == TOK_RANGE ) {
1.303 + READ(TOK_INTEGER);
1.304 + if( yytok.v.i < reg->initValue.i ) {
1.305 + YYPARSE_ERROR( "Invalid mirror target range 0x%x..0x%x", reg->initValue.i, yytok.v.i );
1.306 + }
1.307 + reg->numBytes = yytok.v.i - reg->initValue.i + 1;
1.308 + tok = iolex(TOK_STRIDE);
1.309 + }
1.310 + if( tok == TOK_STRIDE ) {
1.311 + READ(TOK_INTEGER);
1.312 + reg->stride = yytok.v.i;
1.313 + tok = iolex(TOK_ACTION);
1.314 + } else {
1.315 + reg->stride = reg->numBytes;
1.316 + }
1.317 +
1.318 + if( tok != TOK_SEMI ) {
1.319 + YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] );
1.320 + }
1.321 +
1.322 + if( reg->stride < reg->numBytes ) {
1.323 + YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes );
1.324 + }
1.325 +
1.326 + if( rangeOffset != reg->offset ) {
1.327 + reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride;
1.328 + }
1.329 +
1.330 + } else {
1.331 + READ(TOK_IDENTIFIER);
1.332 + reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES));
1.333 + if( reg->type == -1 ) {
1.334 + YYPARSE_ERROR( "Unknown register type '%s'", yystrdup() );
1.335 + }
1.336 + reg->numBytes = TYPE_SIZES[reg->type];
1.337 + tok = iolex(TOK_IDENTIFIER);
1.338 + if( tok == TOK_IDENTIFIER ) {
1.339 + reg->name = yystrdup();
1.340 + tok = iolex(TOK_ACTION);
1.341 + }
1.342 + if( tok == TOK_STRING ) {
1.343 + reg->description = yytok.v.s;
1.344 + tok = iolex(TOK_ACTION);
1.345 + }
1.346 + if( tok == TOK_LPAREN ) {
1.347 + ioparse_regflags(&reg->flags, &reg->numBytes);
1.348 + tok = iolex(TOK_ACTION);
1.349 + }
1.350 + if( tok == TOK_EQUALS ) {
1.351 + tok = iolex(TOK_INTEGER);
1.352 + if( tok == TOK_UNDEFINED ) {
1.353 + reg->initValue.i = 0xDEADBEEFDEADBEEF;
1.354 + reg->initUndefined = TRUE;
1.355 + } else {
1.356 + ioparse_apval(tok, &reg->initValue, &reg->numBytes);
1.357 + }
1.358 + tok = iolex(TOK_ACTION);
1.359 + } else if( reg->type == REG_STRING ) {
1.360 + YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')",0 );
1.361 + }
1.362 + if( tok == TOK_ACTION ) {
1.363 + // reg->action = yystrdup();
1.364 + } else if( tok != TOK_SEMI ) {
1.365 + YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] );
1.366 + }
1.367 +
1.368 + if( rangeOffset != reg->offset ) {
1.369 + reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes;
1.370 + }
1.371 + }
1.372 +
1.373 +}
1.374 +
1.375 +static struct regblock *ioparse_regblock( )
1.376 +{
1.377 + unsigned regsAllocSize = 128;
1.378 + struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
1.379 + block->numRegs = 0;
1.380 +
1.381 + READ(TOK_IDENTIFIER);
1.382 + block->name = yystrdup();
1.383 + int tok = iolex(TOK_AT);
1.384 + if( tok == TOK_STRING ) {
1.385 + block->description = yytok.v.s;
1.386 + tok = iolex(TOK_AT);
1.387 + }
1.388 + if( tok != TOK_AT ) {
1.389 + YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] );
1.390 + }
1.391 + READ(TOK_INTEGER);
1.392 + block->address = yytok.v.i;
1.393 +
1.394 + tok = iolex(TOK_LBRACE);
1.395 + if( tok == TOK_LPAREN) {
1.396 + ioparse_regflags(&block->flags, NULL);
1.397 + READ(TOK_LBRACE);
1.398 + } else if( tok != TOK_LBRACE ) {
1.399 + YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] );
1.400 + }
1.401 +
1.402 + tok = iolex(TOK_INTEGER);
1.403 + while( tok != TOK_RBRACE ) {
1.404 + if( tok != TOK_INTEGER ) {
1.405 + YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]);
1.406 + }
1.407 + struct regdef *regdef = g_malloc0(sizeof(struct regdef));
1.408 + if( block->numRegs >= regsAllocSize ) {
1.409 + regsAllocSize *= 2;
1.410 + block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
1.411 + }
1.412 + block->regs[block->numRegs++] = regdef;
1.413 + ioparse_regdef(regdef);
1.414 +
1.415 + tok = iolex(TOK_INTEGER);
1.416 + }
1.417 +
1.418 + qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb );
1.419 +
1.420 + return block;
1.421 +}
1.422 +
1.423 +GList *ioparse( const char *filename, GList *list )
1.424 +{
1.425 + GList *blocks = list;
1.426 + int count = 0;
1.427 +
1.428 + if( iolex_open(filename) != 0 )
1.429 + return blocks;
1.430 +
1.431 + int tok;
1.432 + do {
1.433 + tok = iolex(TOK_REGISTERS);
1.434 + if( tok == TOK_EOF ) {
1.435 + return blocks;
1.436 + } else if( tok == TOK_INCLUDE) {
1.437 + READ(TOK_STRING);
1.438 +
1.439 + } else if( tok == TOK_SPACE ) {
1.440 + } else if( tok == TOK_REGISTERS ) {
1.441 + struct regblock *block = ioparse_regblock(block);
1.442 + count++;
1.443 + blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb);
1.444 + } else {
1.445 + YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] );
1.446 + }
1.447 + } while( tok != TOK_EOF );
1.448 +
1.449 + iolex_close();
1.450 + if( count == 0 ) {
1.451 + fprintf( stderr, "Warning: input file '%s' did not contain any register definitions\n" );
1.452 + }
1.453 +
1.454 + return blocks;
1.455 +}
1.456 +
1.457 +/**************************** Lexical analyser ***************************/
1.458 +
1.459 +static int iolex_open( const char *filename )
1.460 +{
1.461 + struct stat st;
1.462 + int fd = open( filename, O_RDONLY );
1.463 + if( fd == -1 ) {
1.464 + fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) );
1.465 + return -1;
1.466 + }
1.467 +
1.468 + if( fstat( fd, &st ) != 0 ) {
1.469 + fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) );
1.470 + close(fd);
1.471 + return -1;
1.472 + }
1.473 +
1.474 + char *data = g_malloc( st.st_size + 1 );
1.475 + if( read(fd, data, st.st_size) != st.st_size ) {
1.476 + fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) );
1.477 + close(fd);
1.478 + return -1;
1.479 + }
1.480 + close(fd);
1.481 + data[st.st_size] = 0;
1.482 +
1.483 + yybuffer = yyposn = data;
1.484 + yyend = data + st.st_size;
1.485 + yyline = 1;
1.486 + yylineposn = yyposn;
1.487 + return 0;
1.488 +}
1.489 +
1.490 +static void iolex_close()
1.491 +{
1.492 + g_free(yybuffer);
1.493 + yybuffer = yyend = NULL;
1.494 +}
1.495 +
1.496 +#define YYRETURN(x) do{ \
1.497 + yytok.yylength = yyposn - yystart; \
1.498 + return (x); \
1.499 +} while(0)
1.500 +
1.501 +static int iolex_readhex(char *p, int digits)
1.502 +{
1.503 + int result = 0;
1.504 + for( int i=0; i < digits; i++ ) {
1.505 + result <<= 4;
1.506 + if( isdigit(p[i]) ) {
1.507 + result += p[i]-'0';
1.508 + } else if( p[i] >= 'a' && p[i] <= 'f' ) {
1.509 + result += p[i]-'a'+10;
1.510 + } else if( p[i] >= 'A' && p[i] <= 'F' ) {
1.511 + result += p[i]-'A'+10;
1.512 + } else {
1.513 + return (result >> 4);
1.514 + }
1.515 +
1.516 + }
1.517 + return result;
1.518 +}
1.519 +
1.520 +static int iolex_readoctal(char *p, int digits)
1.521 +{
1.522 + int result = 0;
1.523 + for( int i=0; i < digits; i++ ) {
1.524 + result <<= 3;
1.525 + if( p[i] >= '0' && p[i] <= '7' ) {
1.526 + result += p[i]-'0';
1.527 + } else {
1.528 + return (result >> 4);
1.529 + }
1.530 + }
1.531 + return result;
1.532 +}
1.533 +
1.534 +/**
1.535 + * Copy and interpret the string segment as a C string, replacing escape
1.536 + * expressions with the corresponding value.
1.537 + */
1.538 +static char *iolex_getcstring( char *start, char *end, int *len )
1.539 +{
1.540 + char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */
1.541 + char *q = result;
1.542 +
1.543 + for( char *p = start; p != end; p++ ) {
1.544 + if( *p == '\\' ) {
1.545 + if( ++p == end ) {
1.546 + *q++ = '\\';
1.547 + break;
1.548 + }
1.549 + if( p[0] >= '0' && p[0] <= '3' && p+3 <= end &&
1.550 + p[1] >= '0' && p[1] <= '7' &&
1.551 + p[2] >= '0' && p[2] <= '7' ) {
1.552 + *q++ = (char)iolex_readoctal(p,3);
1.553 + p+=2;
1.554 + } else {
1.555 + switch( *p ) {
1.556 + case 'n':
1.557 + *q++ = '\n';
1.558 + break;
1.559 + case 'r':
1.560 + *q++ = '\r';
1.561 + break;
1.562 + case 't':
1.563 + *q++ = '\t';
1.564 + break;
1.565 + case 'x': /* hex */
1.566 + if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) {
1.567 + *q++ = '\\';
1.568 + *q++ = *p;
1.569 + } else {
1.570 + *q++ = (char)iolex_readhex(p+1, 2);
1.571 + p+=2;
1.572 + }
1.573 + break;
1.574 + default:
1.575 + *q++ = '\\';
1.576 + *q++ = *p;
1.577 + }
1.578 + }
1.579 + } else {
1.580 + *q++ = *p;
1.581 + }
1.582 + }
1.583 + *len = q - result;
1.584 + return result;
1.585 +}
1.586 +
1.587 +int iolex( int expectToken )
1.588 +{
1.589 + int count = 0;
1.590 + while( yyposn < yyend ) {
1.591 + char *yystart = yytok.yytext = yyposn;
1.592 + yytok.yylength = 1;
1.593 + yytok.yyline = yyline;
1.594 + yytok.yycol = yyposn - yylineposn+1;
1.595 + int ch = *yyposn++;
1.596 + if( isdigit(ch) ) {
1.597 + /* INTEGER */
1.598 + if( ch == '0' ) {
1.599 + if( *yyposn == 'x' ) {
1.600 + while( yyposn < yyend && isxdigit(*++yyposn) ) ;
1.601 + } else {
1.602 + while( yyposn < yyend && *yyposn >= '0' && *yyposn <= '7' )
1.603 + yyposn++;
1.604 + }
1.605 + } else {
1.606 + while( yyposn < yyend && isdigit(*yyposn) )
1.607 + yyposn++;
1.608 + }
1.609 + yytok.v.i = strtol( yystart, NULL, 0 );
1.610 + YYRETURN(TOK_INTEGER);
1.611 + } else if( isalpha(ch) || ch == '_' ) {
1.612 + /* IDENTIFIER */
1.613 + while( yyposn < yyend && (isalnum(*yyposn) || *yyposn == '_') )
1.614 + yyposn++;
1.615 + yytok.yylength = yyposn - yystart;
1.616 + if( expectToken == TOK_IDENTIFIER ) {
1.617 + YYRETURN(TOK_IDENTIFIER);
1.618 + }
1.619 + /* Otherwise check for keywords */
1.620 + for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) {
1.621 + if( strlen(TOKEN_NAMES[i]) == yytok.yylength &&
1.622 + strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) {
1.623 + YYRETURN(i);
1.624 + }
1.625 + }
1.626 + YYRETURN(TOK_IDENTIFIER);
1.627 + } else if( isspace(ch) ) {
1.628 + if( ch == '\n' ) {
1.629 + yyline++;
1.630 + yylineposn = yyposn;
1.631 + }
1.632 + while( isspace(*yyposn) ) {
1.633 + if( *yyposn == '\n' ) {
1.634 + yyline++;
1.635 + yylineposn = yyposn+1;
1.636 + }
1.637 + yyposn++;
1.638 + }
1.639 + } else {
1.640 + switch( ch ) {
1.641 + case '(': YYRETURN(TOK_LPAREN);
1.642 + case ')': YYRETURN(TOK_RPAREN);
1.643 + case '[': YYRETURN(TOK_LSQUARE);
1.644 + case ']': YYRETURN(TOK_RSQUARE);
1.645 + case ',': YYRETURN(TOK_COMMA);
1.646 + case ':': YYRETURN(TOK_COLON);
1.647 + case ';': YYRETURN(TOK_SEMI);
1.648 + case '=': YYRETURN(TOK_EQUALS);
1.649 + case '/':
1.650 + if( *yyposn == '/' ) { /* Line comment */
1.651 + while( yyposn < yyend && *++yyposn != '\n' ) ;
1.652 + } else if( *yyposn == '*' ) { /* C comment */
1.653 + while( yyposn < yyend && (*++yyposn != '*' || *++yyposn != '/' ) ) {
1.654 + if( *yyposn == '\n' ) {
1.655 + yyline++;
1.656 + yylineposn = yyposn+1;
1.657 + }
1.658 + }
1.659 + }
1.660 + break;
1.661 + case '\'': /* STRING */
1.662 + while( *yyposn != '\'' ) {
1.663 + if( *yyposn == '\n' ) {
1.664 + fprintf( stderr, "Unexpected newline in string constant!\n" );
1.665 + YYRETURN(TOK_ERROR);
1.666 + } else if( yyposn >= yyend ) {
1.667 + fprintf( stderr, "Unexpected EOF in string constant!\n" );
1.668 + YYRETURN(TOK_ERROR);
1.669 + } else if( *yyposn == '\\' && yyposn[1] == '\'' ) {
1.670 + yyposn++;
1.671 + }
1.672 + yyposn++;
1.673 + }
1.674 + yyposn++;
1.675 + yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen);
1.676 + YYRETURN(TOK_STRING);
1.677 + case '\"': /* STRING */
1.678 + while( *yyposn != '\"' ) {
1.679 + if( *yyposn == '\n' ) {
1.680 + fprintf( stderr, "Unexpected newline in string constant!\n" );
1.681 + YYRETURN(TOK_ERROR);
1.682 + } else if( yyposn >= yyend ) {
1.683 + fprintf( stderr, "Unexpected EOF in string constant!\n" );
1.684 + YYRETURN(TOK_ERROR);
1.685 + } else if( *yyposn == '\\' && yyposn[1] == '\"' ) {
1.686 + yyposn++;
1.687 + }
1.688 + yyposn++;
1.689 + }
1.690 + yyposn++;
1.691 + yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen);
1.692 + YYRETURN(TOK_STRING);
1.693 + case '}':
1.694 + YYRETURN(TOK_RBRACE);
1.695 + case '{': /* ACTION or LBRACE */
1.696 + if( expectToken == TOK_LBRACE ) {
1.697 + YYRETURN(TOK_LBRACE);
1.698 + } else {
1.699 + count++;
1.700 + while( count > 0 && yyposn < yyend ) {
1.701 + if( *yyposn == '{' )
1.702 + count++;
1.703 + if( *yyposn == '}' )
1.704 + count--;
1.705 + yyposn++;
1.706 + }
1.707 + YYRETURN(TOK_ACTION);
1.708 + }
1.709 + case '.':
1.710 + if( *yyposn == '.' ) {
1.711 + yyposn++;
1.712 + YYRETURN(TOK_RANGE);
1.713 + } else {
1.714 + YYRETURN(TOK_PERIOD);
1.715 + }
1.716 + default:
1.717 + fprintf( stderr, "Illegal character: '%c'\n", ch );
1.718 + YYRETURN(TOK_ERROR);
1.719 + }
1.720 + }
1.721 +
1.722 + }
1.723 + return TOK_EOF;
1.724 +}
.