nkeynes@1104: /** nkeynes@1104: * $Id$ nkeynes@1104: * nkeynes@1104: * genmmio I/O register definition parser. nkeynes@1104: * nkeynes@1104: * Copyright (c) 2010 Nathan Keynes. nkeynes@1104: * nkeynes@1104: * This program is free software; you can redistribute it and/or modify nkeynes@1104: * it under the terms of the GNU General Public License as published by nkeynes@1104: * the Free Software Foundation; either version 2 of the License, or nkeynes@1104: * (at your option) any later version. nkeynes@1104: * nkeynes@1104: * This program is distributed in the hope that it will be useful, nkeynes@1104: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1104: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1104: * GNU General Public License for more details. nkeynes@1104: */ nkeynes@1104: nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: nkeynes@1104: #include "genmach.h" nkeynes@1104: /* nkeynes@1104: * Grammar: nkeynes@1104: * nkeynes@1104: * start: start register_block | ; nkeynes@1104: * register_block: 'registers' IDENTIFIER 'at' INTEGER opt_paramlist '{' register_list '}'; nkeynes@1104: * opt_paramlist: '(' param_list ')' | '(' ')' | ; nkeynes@1104: * param_list: param_list ',' param | param ; nkeynes@1104: * param: access_param | fill_param | endian_param | mask_param ; nkeynes@1104: * access_param: 'access' '=' 'any' // Behaves as if it were memory nkeynes@1104: * | 'access' '=' 'nooffset' // Recognize any size access (zero-ext or trunc) as long as the address is exactly the same nkeynes@1104: * | 'access' '=' 'exact' // Recognize only the exact access type at the exact address nkeynes@1104: * ; nkeynes@1104: * fill_param: 'fill' '=' literal; nkeynes@1104: * endian_param: 'endian' '=' 'little' nkeynes@1104: * | 'endian' '=' 'big' nkeynes@1104: * ; nkeynes@1104: * mask_param: 'mask' '=' literal; nkeynes@1104: * nkeynes@1104: * register_list: register_list register_desc | ; nkeynes@1104: * register_desc: INTEGER ':' mode_spec type_spec IDENTIFIER opt_paramlist [ '=' literal ] [ ACTION ] nkeynes@1104: * mode_spec: 'const' | 'rw' | 'ro' | 'wo'; nkeynes@1104: * type_spec: 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64' | 'string' | 'cstring'; nkeynes@1104: * literal: INTEGER | STRING | 'undefined' ; nkeynes@1104: * nkeynes@1104: * C-style comments are recognized as such. nkeynes@1104: */ nkeynes@1104: nkeynes@1104: #define TOK_IDENTIFIER 1 nkeynes@1104: #define TOK_INTEGER 2 nkeynes@1104: #define TOK_STRING 3 nkeynes@1104: #define TOK_ACTION 4 nkeynes@1104: #define TOK_EOF 5 nkeynes@1104: #define TOK_ERROR 6 nkeynes@1104: #define TOK_LPAREN 7 nkeynes@1104: #define TOK_RPAREN 8 nkeynes@1104: #define TOK_LBRACE 9 nkeynes@1104: #define TOK_RBRACE 10 nkeynes@1104: #define TOK_COMMA 11 nkeynes@1104: #define TOK_COLON 12 nkeynes@1104: #define TOK_SEMI 13 nkeynes@1104: #define TOK_PERIOD 14 nkeynes@1104: #define TOK_RANGE 15 nkeynes@1104: #define TOK_LSQUARE 16 nkeynes@1104: #define TOK_RSQUARE 17 nkeynes@1104: #define TOK_EQUALS 18 nkeynes@1104: #define TOK_ACCESS 19 nkeynes@1104: #define TOK_AT 20 nkeynes@1104: #define TOK_ENDIAN 21 nkeynes@1104: #define TOK_FILL 22 nkeynes@1104: #define TOK_INCLUDE 23 nkeynes@1104: #define TOK_MASK 24 nkeynes@1104: #define TOK_REGISTERS 25 nkeynes@1104: #define TOK_SPACE 26 nkeynes@1104: #define TOK_STRIDE 27 nkeynes@1104: #define TOK_TEST 28 nkeynes@1104: #define TOK_TRACE 29 nkeynes@1104: #define TOK_UNDEFINED 30 nkeynes@1104: nkeynes@1104: nkeynes@1104: #define FIRST_KEYWORD TOK_ACCESS nkeynes@1104: #define LAST_KEYWORD TOK_UNDEFINED nkeynes@1104: static const char *TOKEN_NAMES[] = {"NULL", "IDENTIFIER", "INTEGER", "STRING", "ACTION", "EOF", "ERROR", nkeynes@1104: "(", ")", "{", "}", ",", ":", ";", ".", "..", "[", "]", "=", nkeynes@1104: "ACCESS", "AT", "ENDIAN", "FILL", "INCLUDE", "MASK", "REGISTERS", "SPACE", "STRIDE", "TEST", "TRACE", "UNDEFINED" }; nkeynes@1104: static const char *MODE_NAMES[] = {"const", "rw", "ro", "wo", "mirror"}; nkeynes@1104: static const char *TYPE_NAMES[] = {"i8", "i16", "i32", "i64", "f32", "f64", "string"}; nkeynes@1104: static int TYPE_SIZES[] = {1,2,4,8,4,8,0}; nkeynes@1104: static const char *ENDIAN_NAMES[] = {"default", "little", "big"}; nkeynes@1104: static const char *ACCESS_NAMES[] = {"default", "any", "nooffset", "exact"}; nkeynes@1104: static const char *TRACE_NAMES[] = {"default", "off", "on"}; nkeynes@1104: nkeynes@1104: #define elementsof(x) (sizeof(x)/sizeof(x[0])) nkeynes@1104: nkeynes@1104: typedef struct token_data { nkeynes@1104: char *yytext; nkeynes@1104: int yylength; nkeynes@1104: int yyline; nkeynes@1104: int yycol; nkeynes@1104: union { nkeynes@1104: long i; nkeynes@1104: char *s; nkeynes@1104: } v; nkeynes@1104: int slen; nkeynes@1104: } token_data; nkeynes@1104: nkeynes@1104: static char *yybuffer; nkeynes@1104: static char *yyposn, *yylineposn; nkeynes@1104: static char *yyend; nkeynes@1104: static int yyline; nkeynes@1104: static struct token_data yytok; nkeynes@1104: nkeynes@1104: #define YYPARSE_ERROR( msg, ... ) \ nkeynes@1104: do { \ nkeynes@1104: fprintf( stderr, "Parse error at %d:%d: " msg "\n", yytok.yyline, yytok.yycol, __VA_ARGS__ ); \ nkeynes@1104: exit(2); \ nkeynes@1104: } while(0) nkeynes@1104: nkeynes@1104: #define READ(x) \ nkeynes@1104: { int _tok = iolex(x); \ nkeynes@1104: if( _tok != (x) ) { \ nkeynes@1104: YYPARSE_ERROR( "Expected %s but got %s", TOKEN_NAMES[x], TOKEN_NAMES[_tok] ); \ nkeynes@1104: } \ nkeynes@1104: } nkeynes@1104: nkeynes@1104: static int iolex( int expectToken ); nkeynes@1104: static int iolex_open( const char *filename ); nkeynes@1104: static void iolex_close(); nkeynes@1104: nkeynes@1104: static inline char *yystrdup() nkeynes@1104: { nkeynes@1104: char *str = g_malloc0(yytok.yylength+1); nkeynes@1104: memcpy( str, yytok.yytext, yytok.yylength); nkeynes@1104: return str; nkeynes@1104: } nkeynes@1104: nkeynes@1104: static inline int yystrcasecmp(const char *cmp) nkeynes@1104: { nkeynes@1104: int len = strlen(cmp); nkeynes@1104: if( len != yytok.yylength ) { nkeynes@1104: return yytok.yylength - len; nkeynes@1104: } nkeynes@1104: return strncasecmp(yytok.yytext, cmp, yytok.yylength); nkeynes@1104: } nkeynes@1104: nkeynes@1104: static int yymatch( const char *arr[], unsigned numOptions ) nkeynes@1104: { nkeynes@1104: for( unsigned i=0; iaddress - blockb->address; nkeynes@1104: } nkeynes@1104: nkeynes@1104: static int register_ptr_sort_cb( const void *a, const void *b ) nkeynes@1104: { nkeynes@1104: regdef_t *ptra = (regdef_t *)a; nkeynes@1104: regdef_t *ptrb = (regdef_t *)b; nkeynes@1104: if( (*ptra)->offset != (*ptrb)->offset ) nkeynes@1104: return (*ptra)->offset - (*ptrb)->offset; nkeynes@1104: return (*ptra)->type - (*ptrb)->type; nkeynes@1104: } nkeynes@1104: nkeynes@1104: static void ioparse_apval(int tok, union apval *apv, unsigned *numBytes) nkeynes@1104: { nkeynes@1104: if( tok == TOK_INTEGER ) { nkeynes@1104: apv->i = yytok.v.i; nkeynes@1104: if( *numBytes == 0 ) { nkeynes@1104: YYPARSE_ERROR( "Expected string initializer but was an integer (0x%x)", yytok.v.i ); nkeynes@1104: } nkeynes@1104: } else if( tok = TOK_STRING ) { nkeynes@1104: if( *numBytes == 0 ) { nkeynes@1104: *numBytes = yytok.slen; nkeynes@1104: apv->s = yytok.v.s; nkeynes@1104: } else { nkeynes@1104: if( *numBytes != yytok.slen ) { nkeynes@1104: YYPARSE_ERROR( "Expected %d byte initializer but was %d", *numBytes, yytok.slen ); nkeynes@1104: } nkeynes@1104: assert( *numBytes < sizeof(uint64_t) ); nkeynes@1104: apv->i = 0; nkeynes@1104: /* FIXME: handle endian mismatches */ nkeynes@1104: memcpy( &apv->i, yytok.v.s, yytok.slen ); nkeynes@1104: } nkeynes@1104: } else { nkeynes@1104: YYPARSE_ERROR( "Expected literal (integer or string), but got %s", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: static void ioparse_regflags( regflags_t flags, unsigned *numBytes ) nkeynes@1104: { nkeynes@1104: do { nkeynes@1104: int tok = iolex(TOK_RPAREN); nkeynes@1104: switch(tok) { nkeynes@1104: case TOK_FILL: nkeynes@1104: READ(TOK_EQUALS); nkeynes@1104: tok = iolex(TOK_INTEGER); nkeynes@1104: flags->fillSizeBytes = 4; nkeynes@1104: ioparse_apval( tok, &flags->fillValue, &flags->fillSizeBytes ); nkeynes@1104: break; nkeynes@1104: case TOK_ACCESS: nkeynes@1104: READ(TOK_EQUALS); nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES)); nkeynes@1104: if( flags->access == -1 ) { nkeynes@1104: YYPARSE_ERROR("Unknown access mode '%s'", yystrdup()); nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: case TOK_MASK: nkeynes@1104: if( numBytes ) { nkeynes@1104: READ(TOK_EQUALS); nkeynes@1104: tok = iolex(TOK_INTEGER); nkeynes@1104: ioparse_apval( tok, &flags->maskValue, numBytes ); nkeynes@1104: } else { nkeynes@1104: YYPARSE_ERROR("mask is not valid on a register block",0); nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: case TOK_ENDIAN: nkeynes@1104: READ(TOK_EQUALS); nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES)); nkeynes@1104: if( flags->endian == -1 ) { nkeynes@1104: YYPARSE_ERROR("Unknown endianness '%s'", yystrdup()); nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: case TOK_TRACE: nkeynes@1104: READ(TOK_EQUALS); nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES)); nkeynes@1104: if( flags->traceFlag == -1 ) { nkeynes@1104: YYPARSE_ERROR("Unknown trace flag '%s'", yystrdup()); nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: case TOK_TEST: nkeynes@1104: READ(TOK_EQUALS); nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES)); nkeynes@1104: if( flags->testFlag == -1 ) { nkeynes@1104: YYPARSE_ERROR("Unknown test flag '%s'", yystrdup()); nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: case TOK_COMMA: nkeynes@1104: break; nkeynes@1104: case TOK_RPAREN: nkeynes@1104: return; nkeynes@1104: default: nkeynes@1104: YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]); nkeynes@1104: } nkeynes@1104: } while(1); nkeynes@1104: } nkeynes@1104: nkeynes@1104: static void ioparse_regdef( struct regdef *reg ) nkeynes@1104: { nkeynes@1104: reg->offset = yytok.v.i; nkeynes@1104: reg->numElements = 1; nkeynes@1104: reg->numBytes = 0; nkeynes@1104: reg->initValue.i = 0; nkeynes@1104: reg->flags.maskValue.i = -1; nkeynes@1104: unsigned rangeOffset = reg->offset; nkeynes@1104: nkeynes@1104: int tok = iolex(TOK_COLON); nkeynes@1104: if( tok == TOK_RANGE ) { nkeynes@1104: READ(TOK_INTEGER); nkeynes@1104: rangeOffset = yytok.v.i; nkeynes@1104: if( rangeOffset < reg->offset ) { nkeynes@1104: YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset ); nkeynes@1104: } nkeynes@1104: READ(TOK_COLON); nkeynes@1104: } else if( tok != TOK_COLON ) { nkeynes@1104: YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES)); nkeynes@1104: if( reg->mode == -1 ) { nkeynes@1104: YYPARSE_ERROR( "Unknown register mode '%s'", yystrdup() ); nkeynes@1104: } nkeynes@1104: if( reg->mode == REG_MIRROR ) { nkeynes@1104: /* Mirror regions have a target range only */ nkeynes@1104: READ(TOK_INTEGER); nkeynes@1104: reg->initValue.i = yytok.v.i; nkeynes@1104: reg->type = REG_I8; nkeynes@1104: tok = iolex(TOK_RANGE); nkeynes@1104: if( tok == TOK_RANGE ) { nkeynes@1104: READ(TOK_INTEGER); nkeynes@1104: if( yytok.v.i < reg->initValue.i ) { nkeynes@1104: YYPARSE_ERROR( "Invalid mirror target range 0x%x..0x%x", reg->initValue.i, yytok.v.i ); nkeynes@1104: } nkeynes@1104: reg->numBytes = yytok.v.i - reg->initValue.i + 1; nkeynes@1104: tok = iolex(TOK_STRIDE); nkeynes@1104: } nkeynes@1104: if( tok == TOK_STRIDE ) { nkeynes@1104: READ(TOK_INTEGER); nkeynes@1104: reg->stride = yytok.v.i; nkeynes@1104: tok = iolex(TOK_ACTION); nkeynes@1104: } else { nkeynes@1104: reg->stride = reg->numBytes; nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( tok != TOK_SEMI ) { nkeynes@1104: YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( reg->stride < reg->numBytes ) { nkeynes@1104: YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( rangeOffset != reg->offset ) { nkeynes@1104: reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride; nkeynes@1104: } nkeynes@1104: nkeynes@1104: } else { nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES)); nkeynes@1104: if( reg->type == -1 ) { nkeynes@1104: YYPARSE_ERROR( "Unknown register type '%s'", yystrdup() ); nkeynes@1104: } nkeynes@1104: reg->numBytes = TYPE_SIZES[reg->type]; nkeynes@1104: tok = iolex(TOK_IDENTIFIER); nkeynes@1104: if( tok == TOK_IDENTIFIER ) { nkeynes@1104: reg->name = yystrdup(); nkeynes@1104: tok = iolex(TOK_ACTION); nkeynes@1104: } nkeynes@1104: if( tok == TOK_STRING ) { nkeynes@1104: reg->description = yytok.v.s; nkeynes@1104: tok = iolex(TOK_ACTION); nkeynes@1104: } nkeynes@1104: if( tok == TOK_LPAREN ) { nkeynes@1104: ioparse_regflags(®->flags, ®->numBytes); nkeynes@1104: tok = iolex(TOK_ACTION); nkeynes@1104: } nkeynes@1104: if( tok == TOK_EQUALS ) { nkeynes@1104: tok = iolex(TOK_INTEGER); nkeynes@1104: if( tok == TOK_UNDEFINED ) { nkeynes@1104: reg->initValue.i = 0xDEADBEEFDEADBEEF; nkeynes@1104: reg->initUndefined = TRUE; nkeynes@1104: } else { nkeynes@1104: ioparse_apval(tok, ®->initValue, ®->numBytes); nkeynes@1104: } nkeynes@1104: tok = iolex(TOK_ACTION); nkeynes@1104: } else if( reg->type == REG_STRING ) { nkeynes@1104: YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')",0 ); nkeynes@1104: } nkeynes@1104: if( tok == TOK_ACTION ) { nkeynes@1104: // reg->action = yystrdup(); nkeynes@1104: } else if( tok != TOK_SEMI ) { nkeynes@1104: YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( rangeOffset != reg->offset ) { nkeynes@1104: reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: } nkeynes@1104: nkeynes@1104: static struct regblock *ioparse_regblock( ) nkeynes@1104: { nkeynes@1104: unsigned regsAllocSize = 128; nkeynes@1104: struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize); nkeynes@1104: block->numRegs = 0; nkeynes@1104: nkeynes@1104: READ(TOK_IDENTIFIER); nkeynes@1104: block->name = yystrdup(); nkeynes@1104: int tok = iolex(TOK_AT); nkeynes@1104: if( tok == TOK_STRING ) { nkeynes@1104: block->description = yytok.v.s; nkeynes@1104: tok = iolex(TOK_AT); nkeynes@1104: } nkeynes@1104: if( tok != TOK_AT ) { nkeynes@1104: YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: READ(TOK_INTEGER); nkeynes@1104: block->address = yytok.v.i; nkeynes@1104: nkeynes@1104: tok = iolex(TOK_LBRACE); nkeynes@1104: if( tok == TOK_LPAREN) { nkeynes@1104: ioparse_regflags(&block->flags, NULL); nkeynes@1104: READ(TOK_LBRACE); nkeynes@1104: } else if( tok != TOK_LBRACE ) { nkeynes@1104: YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: tok = iolex(TOK_INTEGER); nkeynes@1104: while( tok != TOK_RBRACE ) { nkeynes@1104: if( tok != TOK_INTEGER ) { nkeynes@1104: YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]); nkeynes@1104: } nkeynes@1104: struct regdef *regdef = g_malloc0(sizeof(struct regdef)); nkeynes@1104: if( block->numRegs >= regsAllocSize ) { nkeynes@1104: regsAllocSize *= 2; nkeynes@1104: block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize); nkeynes@1104: } nkeynes@1104: block->regs[block->numRegs++] = regdef; nkeynes@1104: ioparse_regdef(regdef); nkeynes@1104: nkeynes@1104: tok = iolex(TOK_INTEGER); nkeynes@1104: } nkeynes@1104: nkeynes@1104: qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb ); nkeynes@1104: nkeynes@1104: return block; nkeynes@1104: } nkeynes@1104: nkeynes@1104: GList *ioparse( const char *filename, GList *list ) nkeynes@1104: { nkeynes@1104: GList *blocks = list; nkeynes@1104: int count = 0; nkeynes@1104: nkeynes@1104: if( iolex_open(filename) != 0 ) nkeynes@1104: return blocks; nkeynes@1104: nkeynes@1104: int tok; nkeynes@1104: do { nkeynes@1104: tok = iolex(TOK_REGISTERS); nkeynes@1104: if( tok == TOK_EOF ) { nkeynes@1104: return blocks; nkeynes@1104: } else if( tok == TOK_INCLUDE) { nkeynes@1104: READ(TOK_STRING); nkeynes@1104: nkeynes@1104: } else if( tok == TOK_SPACE ) { nkeynes@1104: } else if( tok == TOK_REGISTERS ) { nkeynes@1104: struct regblock *block = ioparse_regblock(block); nkeynes@1104: count++; nkeynes@1104: blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb); nkeynes@1104: } else { nkeynes@1104: YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] ); nkeynes@1104: } nkeynes@1104: } while( tok != TOK_EOF ); nkeynes@1104: nkeynes@1104: iolex_close(); nkeynes@1104: if( count == 0 ) { nkeynes@1104: fprintf( stderr, "Warning: input file '%s' did not contain any register definitions\n" ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: return blocks; nkeynes@1104: } nkeynes@1104: nkeynes@1104: /**************************** Lexical analyser ***************************/ nkeynes@1104: nkeynes@1104: static int iolex_open( const char *filename ) nkeynes@1104: { nkeynes@1104: struct stat st; nkeynes@1104: int fd = open( filename, O_RDONLY ); nkeynes@1104: if( fd == -1 ) { nkeynes@1104: fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) ); nkeynes@1104: return -1; nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( fstat( fd, &st ) != 0 ) { nkeynes@1104: fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) ); nkeynes@1104: close(fd); nkeynes@1104: return -1; nkeynes@1104: } nkeynes@1104: nkeynes@1104: char *data = g_malloc( st.st_size + 1 ); nkeynes@1104: if( read(fd, data, st.st_size) != st.st_size ) { nkeynes@1104: fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) ); nkeynes@1104: close(fd); nkeynes@1104: return -1; nkeynes@1104: } nkeynes@1104: close(fd); nkeynes@1104: data[st.st_size] = 0; nkeynes@1104: nkeynes@1104: yybuffer = yyposn = data; nkeynes@1104: yyend = data + st.st_size; nkeynes@1104: yyline = 1; nkeynes@1104: yylineposn = yyposn; nkeynes@1104: return 0; nkeynes@1104: } nkeynes@1104: nkeynes@1104: static void iolex_close() nkeynes@1104: { nkeynes@1104: g_free(yybuffer); nkeynes@1104: yybuffer = yyend = NULL; nkeynes@1104: } nkeynes@1104: nkeynes@1104: #define YYRETURN(x) do{ \ nkeynes@1104: yytok.yylength = yyposn - yystart; \ nkeynes@1104: return (x); \ nkeynes@1104: } while(0) nkeynes@1104: nkeynes@1104: static int iolex_readhex(char *p, int digits) nkeynes@1104: { nkeynes@1104: int result = 0; nkeynes@1104: for( int i=0; i < digits; i++ ) { nkeynes@1104: result <<= 4; nkeynes@1104: if( isdigit(p[i]) ) { nkeynes@1104: result += p[i]-'0'; nkeynes@1104: } else if( p[i] >= 'a' && p[i] <= 'f' ) { nkeynes@1104: result += p[i]-'a'+10; nkeynes@1104: } else if( p[i] >= 'A' && p[i] <= 'F' ) { nkeynes@1104: result += p[i]-'A'+10; nkeynes@1104: } else { nkeynes@1104: return (result >> 4); nkeynes@1104: } nkeynes@1104: nkeynes@1104: } nkeynes@1104: return result; nkeynes@1104: } nkeynes@1104: nkeynes@1104: static int iolex_readoctal(char *p, int digits) nkeynes@1104: { nkeynes@1104: int result = 0; nkeynes@1104: for( int i=0; i < digits; i++ ) { nkeynes@1104: result <<= 3; nkeynes@1104: if( p[i] >= '0' && p[i] <= '7' ) { nkeynes@1104: result += p[i]-'0'; nkeynes@1104: } else { nkeynes@1104: return (result >> 4); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: return result; nkeynes@1104: } nkeynes@1104: nkeynes@1104: /** nkeynes@1104: * Copy and interpret the string segment as a C string, replacing escape nkeynes@1104: * expressions with the corresponding value. nkeynes@1104: */ nkeynes@1104: static char *iolex_getcstring( char *start, char *end, int *len ) nkeynes@1104: { nkeynes@1104: char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */ nkeynes@1104: char *q = result; nkeynes@1104: nkeynes@1104: for( char *p = start; p != end; p++ ) { nkeynes@1104: if( *p == '\\' ) { nkeynes@1104: if( ++p == end ) { nkeynes@1104: *q++ = '\\'; nkeynes@1104: break; nkeynes@1104: } nkeynes@1104: if( p[0] >= '0' && p[0] <= '3' && p+3 <= end && nkeynes@1104: p[1] >= '0' && p[1] <= '7' && nkeynes@1104: p[2] >= '0' && p[2] <= '7' ) { nkeynes@1104: *q++ = (char)iolex_readoctal(p,3); nkeynes@1104: p+=2; nkeynes@1104: } else { nkeynes@1104: switch( *p ) { nkeynes@1104: case 'n': nkeynes@1104: *q++ = '\n'; nkeynes@1104: break; nkeynes@1104: case 'r': nkeynes@1104: *q++ = '\r'; nkeynes@1104: break; nkeynes@1104: case 't': nkeynes@1104: *q++ = '\t'; nkeynes@1104: break; nkeynes@1104: case 'x': /* hex */ nkeynes@1104: if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) { nkeynes@1104: *q++ = '\\'; nkeynes@1104: *q++ = *p; nkeynes@1104: } else { nkeynes@1104: *q++ = (char)iolex_readhex(p+1, 2); nkeynes@1104: p+=2; nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: default: nkeynes@1104: *q++ = '\\'; nkeynes@1104: *q++ = *p; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } else { nkeynes@1104: *q++ = *p; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: *len = q - result; nkeynes@1104: return result; nkeynes@1104: } nkeynes@1104: nkeynes@1104: int iolex( int expectToken ) nkeynes@1104: { nkeynes@1104: int count = 0; nkeynes@1104: while( yyposn < yyend ) { nkeynes@1104: char *yystart = yytok.yytext = yyposn; nkeynes@1104: yytok.yylength = 1; nkeynes@1104: yytok.yyline = yyline; nkeynes@1104: yytok.yycol = yyposn - yylineposn+1; nkeynes@1104: int ch = *yyposn++; nkeynes@1104: if( isdigit(ch) ) { nkeynes@1104: /* INTEGER */ nkeynes@1104: if( ch == '0' ) { nkeynes@1104: if( *yyposn == 'x' ) { nkeynes@1104: while( yyposn < yyend && isxdigit(*++yyposn) ) ; nkeynes@1104: } else { nkeynes@1104: while( yyposn < yyend && *yyposn >= '0' && *yyposn <= '7' ) nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: } else { nkeynes@1104: while( yyposn < yyend && isdigit(*yyposn) ) nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: yytok.v.i = strtol( yystart, NULL, 0 ); nkeynes@1104: YYRETURN(TOK_INTEGER); nkeynes@1104: } else if( isalpha(ch) || ch == '_' ) { nkeynes@1104: /* IDENTIFIER */ nkeynes@1104: while( yyposn < yyend && (isalnum(*yyposn) || *yyposn == '_') ) nkeynes@1104: yyposn++; nkeynes@1104: yytok.yylength = yyposn - yystart; nkeynes@1104: if( expectToken == TOK_IDENTIFIER ) { nkeynes@1104: YYRETURN(TOK_IDENTIFIER); nkeynes@1104: } nkeynes@1104: /* Otherwise check for keywords */ nkeynes@1104: for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) { nkeynes@1104: if( strlen(TOKEN_NAMES[i]) == yytok.yylength && nkeynes@1104: strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) { nkeynes@1104: YYRETURN(i); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: YYRETURN(TOK_IDENTIFIER); nkeynes@1104: } else if( isspace(ch) ) { nkeynes@1104: if( ch == '\n' ) { nkeynes@1104: yyline++; nkeynes@1104: yylineposn = yyposn; nkeynes@1104: } nkeynes@1104: while( isspace(*yyposn) ) { nkeynes@1104: if( *yyposn == '\n' ) { nkeynes@1104: yyline++; nkeynes@1104: yylineposn = yyposn+1; nkeynes@1104: } nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: } else { nkeynes@1104: switch( ch ) { nkeynes@1104: case '(': YYRETURN(TOK_LPAREN); nkeynes@1104: case ')': YYRETURN(TOK_RPAREN); nkeynes@1104: case '[': YYRETURN(TOK_LSQUARE); nkeynes@1104: case ']': YYRETURN(TOK_RSQUARE); nkeynes@1104: case ',': YYRETURN(TOK_COMMA); nkeynes@1104: case ':': YYRETURN(TOK_COLON); nkeynes@1104: case ';': YYRETURN(TOK_SEMI); nkeynes@1104: case '=': YYRETURN(TOK_EQUALS); nkeynes@1104: case '/': nkeynes@1104: if( *yyposn == '/' ) { /* Line comment */ nkeynes@1104: while( yyposn < yyend && *++yyposn != '\n' ) ; nkeynes@1104: } else if( *yyposn == '*' ) { /* C comment */ nkeynes@1104: while( yyposn < yyend && (*++yyposn != '*' || *++yyposn != '/' ) ) { nkeynes@1104: if( *yyposn == '\n' ) { nkeynes@1104: yyline++; nkeynes@1104: yylineposn = yyposn+1; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } nkeynes@1104: break; nkeynes@1104: case '\'': /* STRING */ nkeynes@1104: while( *yyposn != '\'' ) { nkeynes@1104: if( *yyposn == '\n' ) { nkeynes@1104: fprintf( stderr, "Unexpected newline in string constant!\n" ); nkeynes@1104: YYRETURN(TOK_ERROR); nkeynes@1104: } else if( yyposn >= yyend ) { nkeynes@1104: fprintf( stderr, "Unexpected EOF in string constant!\n" ); nkeynes@1104: YYRETURN(TOK_ERROR); nkeynes@1104: } else if( *yyposn == '\\' && yyposn[1] == '\'' ) { nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: yyposn++; nkeynes@1104: yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen); nkeynes@1104: YYRETURN(TOK_STRING); nkeynes@1104: case '\"': /* STRING */ nkeynes@1104: while( *yyposn != '\"' ) { nkeynes@1104: if( *yyposn == '\n' ) { nkeynes@1104: fprintf( stderr, "Unexpected newline in string constant!\n" ); nkeynes@1104: YYRETURN(TOK_ERROR); nkeynes@1104: } else if( yyposn >= yyend ) { nkeynes@1104: fprintf( stderr, "Unexpected EOF in string constant!\n" ); nkeynes@1104: YYRETURN(TOK_ERROR); nkeynes@1104: } else if( *yyposn == '\\' && yyposn[1] == '\"' ) { nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: yyposn++; nkeynes@1104: yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen); nkeynes@1104: YYRETURN(TOK_STRING); nkeynes@1104: case '}': nkeynes@1104: YYRETURN(TOK_RBRACE); nkeynes@1104: case '{': /* ACTION or LBRACE */ nkeynes@1104: if( expectToken == TOK_LBRACE ) { nkeynes@1104: YYRETURN(TOK_LBRACE); nkeynes@1104: } else { nkeynes@1104: count++; nkeynes@1104: while( count > 0 && yyposn < yyend ) { nkeynes@1104: if( *yyposn == '{' ) nkeynes@1104: count++; nkeynes@1104: if( *yyposn == '}' ) nkeynes@1104: count--; nkeynes@1104: yyposn++; nkeynes@1104: } nkeynes@1104: YYRETURN(TOK_ACTION); nkeynes@1104: } nkeynes@1104: case '.': nkeynes@1104: if( *yyposn == '.' ) { nkeynes@1104: yyposn++; nkeynes@1104: YYRETURN(TOK_RANGE); nkeynes@1104: } else { nkeynes@1104: YYRETURN(TOK_PERIOD); nkeynes@1104: } nkeynes@1104: default: nkeynes@1104: fprintf( stderr, "Illegal character: '%c'\n", ch ); nkeynes@1104: YYRETURN(TOK_ERROR); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: } nkeynes@1104: return TOK_EOF; nkeynes@1104: }