int enum_specifier(void) { match(ENUM); if( lookaheadT.type == ID ) { match(ID); } if( lookaheadT.type == LCURLY ) { match(LCURLY); enumerator_list(); match(RCURLY); } }
/** * @param[in] param source file * @param[in] type #TYPE_C, #TYPE_YACC, #TYPE_LEX */ static void C_family(const struct parser_param *param, int type) { int c, cc; int savelevel; int startmacro, startsharp; const char *interested = "{}=;"; STRBUF *sb = strbuf_open(0); /* * yacc file format is like the following. * * declarations * %% * rules * %% * programs * */ int yaccstatus = (type == TYPE_YACC) ? DECLARATIONS : PROGRAMS; int inC = (type == TYPE_YACC) ? 0 : 1; /* 1 while C source */ level = piflevel = externclevel = 0; savelevel = -1; startmacro = startsharp = 0; if (!opentoken(param->file)) die("'%s' cannot open.", param->file); cmode = 1; /* allow token like '#xxx' */ crflag = 1; /* require '\n' as a token */ if (type == TYPE_YACC) ymode = 1; /* allow token like '%xxx' */ while ((cc = nexttoken(interested, c_reserved_word)) != EOF) { switch (cc) { case SYMBOL: /* symbol */ if (inC && peekc(0) == '('/* ) */) { if (param->isnotfunction(token)) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (level > 0 || startmacro) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (level == 0 && !startmacro && !startsharp) { char arg1[MAXTOKEN], savetok[MAXTOKEN], *saveline; int savelineno = lineno; strlimcpy(savetok, token, sizeof(savetok)); strbuf_reset(sb); strbuf_puts(sb, sp); saveline = strbuf_value(sb); arg1[0] = '\0'; /* * Guile function entry using guile-snarf is like follows: * * SCM_DEFINE (scm_list, "list", 0, 0, 1, * (SCM objs), * "Return a list containing OBJS, the arguments to `list'.") * #define FUNC_NAME s_scm_list * { * return objs; * } * #undef FUNC_NAME * * We should assume the first argument as a function name instead of 'SCM_DEFINE'. */ if (function_definition(param, arg1)) { if (!strcmp(savetok, "SCM_DEFINE") && *arg1) strlimcpy(savetok, arg1, sizeof(savetok)); PUT(PARSER_DEF, savetok, savelineno, saveline); } else { PUT(PARSER_REF_SYM, savetok, savelineno, saveline); } } } else { PUT(PARSER_REF_SYM, token, lineno, sp); } break; case '{': /* } */ DBG_PRINT(level, "{"); /* } */ if (yaccstatus == RULES && level == 0) inC = 1; ++level; if ((param->flags & PARSER_BEGIN_BLOCK) && atfirst) { if ((param->flags & PARSER_WARNING) && level != 1) warning("forced level 1 block start by '{' at column 0 [+%d %s].", lineno, curfile); /* } */ level = 1; } break; /* { */ case '}': if (--level < 0) { if (externclevel > 0) externclevel--; else if (param->flags & PARSER_WARNING) warning("missing left '{' [+%d %s].", lineno, curfile); /* } */ level = 0; } if ((param->flags & PARSER_END_BLOCK) && atfirst) { if ((param->flags & PARSER_WARNING) && level != 0) /* { */ warning("forced level 0 block end by '}' at column 0 [+%d %s].", lineno, curfile); level = 0; } if (yaccstatus == RULES && level == 0) inC = 0; /* { */ DBG_PRINT(level, "}"); break; case '\n': if (startmacro && level != savelevel) { if (param->flags & PARSER_WARNING) warning("different level before and after #define macro. reseted. [+%d %s].", lineno, curfile); level = savelevel; } startmacro = startsharp = 0; break; case YACC_SEP: /* %% */ if (level != 0) { if (param->flags & PARSER_WARNING) warning("forced level 0 block end by '%%' [+%d %s].", lineno, curfile); level = 0; } if (yaccstatus == DECLARATIONS) { PUT(PARSER_DEF, "yyparse", lineno, sp); yaccstatus = RULES; } else if (yaccstatus == RULES) yaccstatus = PROGRAMS; inC = (yaccstatus == PROGRAMS) ? 1 : 0; break; case YACC_BEGIN: /* %{ */ if (level != 0) { if (param->flags & PARSER_WARNING) warning("forced level 0 block end by '%%{' [+%d %s].", lineno, curfile); level = 0; } if (inC == 1 && (param->flags & PARSER_WARNING)) warning("'%%{' appeared in C mode. [+%d %s].", lineno, curfile); inC = 1; break; case YACC_END: /* %} */ if (level != 0) { if (param->flags & PARSER_WARNING) warning("forced level 0 block end by '%%}' [+%d %s].", lineno, curfile); level = 0; } if (inC == 0 && (param->flags & PARSER_WARNING)) warning("'%%}' appeared in Yacc mode. [+%d %s].", lineno, curfile); inC = 0; break; case YACC_UNION: /* %union {...} */ if (yaccstatus == DECLARATIONS) PUT(PARSER_DEF, "YYSTYPE", lineno, sp); break; /* * #xxx */ case SHARP_DEFINE: case SHARP_UNDEF: startmacro = 1; savelevel = level; if ((c = nexttoken(interested, c_reserved_word)) != SYMBOL) { pushbacktoken(); break; } if (peekc(1) == '('/* ) */) { PUT(PARSER_DEF, token, lineno, sp); while ((c = nexttoken("()", c_reserved_word)) != EOF && c != '\n' && c != /* ( */ ')') if (c == SYMBOL) PUT(PARSER_REF_SYM, token, lineno, sp); if (c == '\n') pushbacktoken(); } else { PUT(PARSER_DEF, token, lineno, sp); } break; case SHARP_IMPORT: case SHARP_INCLUDE: case SHARP_INCLUDE_NEXT: case SHARP_ERROR: case SHARP_LINE: case SHARP_PRAGMA: case SHARP_WARNING: case SHARP_IDENT: case SHARP_SCCS: while ((c = nexttoken(interested, c_reserved_word)) != EOF && c != '\n') ; break; case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, cc); break; case SHARP_SHARP: /* ## */ (void)nexttoken(interested, c_reserved_word); break; case C_EXTERN: /* for 'extern "C"/"C++"' */ if (peekc(0) != '"') /* " */ continue; /* If does not start with '"', continue. */ while ((c = nexttoken(interested, c_reserved_word)) == '\n') ; /* * 'extern "C"/"C++"' block is a kind of namespace block. * (It doesn't have any influence on level.) */ if (c == '{') /* } */ externclevel++; else pushbacktoken(); break; case C_STRUCT: case C_ENUM: case C_UNION: while ((c = nexttoken(interested, c_reserved_word)) == C___ATTRIBUTE__) process_attribute(param); if (c == SYMBOL) { if (peekc(0) == '{') /* } */ { PUT(PARSER_DEF, token, lineno, sp); } else { PUT(PARSER_REF_SYM, token, lineno, sp); } c = nexttoken(interested, c_reserved_word); } if (c == '{' /* } */ && cc == C_ENUM) { enumerator_list(param); } else { pushbacktoken(); } break; /* control statement check */ case C_BREAK: case C_CASE: case C_CONTINUE: case C_DEFAULT: case C_DO: case C_ELSE: case C_FOR: case C_GOTO: case C_IF: case C_RETURN: case C_SWITCH: case C_WHILE: if ((param->flags & PARSER_WARNING) && !startmacro && level == 0) warning("Out of function. %8s [+%d %s]", token, lineno, curfile); break; case C_TYPEDEF: { /* * This parser is too complex to maintain. * We should rewrite the whole. */ char savetok[MAXTOKEN]; int savelineno = 0; int typedef_savelevel = level; savetok[0] = 0; /* skip type qualifiers */ do { c = nexttoken("{}(),;", c_reserved_word); } while (IS_TYPE_QUALIFIER(c) || c == '\n'); if ((param->flags & PARSER_WARNING) && c == EOF) { warning("unexpected eof. [+%d %s]", lineno, curfile); break; } else if (c == C_ENUM || c == C_STRUCT || c == C_UNION) { char *interest_enum = "{},;"; int c_ = c; while ((c = nexttoken(interest_enum, c_reserved_word)) == C___ATTRIBUTE__) process_attribute(param); /* read tag name if exist */ if (c == SYMBOL) { if (peekc(0) == '{') /* } */ { PUT(PARSER_DEF, token, lineno, sp); } else { PUT(PARSER_REF_SYM, token, lineno, sp); } c = nexttoken(interest_enum, c_reserved_word); } if (c_ == C_ENUM) { if (c == '{') /* } */ c = enumerator_list(param); else pushbacktoken(); } else { for (; c != EOF; c = nexttoken(interest_enum, c_reserved_word)) { switch (c) { case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, c); continue; default: break; } if (c == ';' && level == typedef_savelevel) { if (savetok[0]) PUT(PARSER_DEF, savetok, savelineno, sp); break; } else if (c == '{') level++; else if (c == '}') { if (--level == typedef_savelevel) break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); /* save lastest token */ strlimcpy(savetok, token, sizeof(savetok)); savelineno = lineno; } } if (c == ';') break; } if ((param->flags & PARSER_WARNING) && c == EOF) { warning("unexpected eof. [+%d %s]", lineno, curfile); break; } } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } savetok[0] = 0; while ((c = nexttoken("(),;", c_reserved_word)) != EOF) { switch (c) { case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, c); continue; default: break; } if (c == '(') level++; else if (c == ')') level--; else if (c == SYMBOL) { if (level > typedef_savelevel) { PUT(PARSER_REF_SYM, token, lineno, sp); } else { /* put latest token if any */ if (savetok[0]) { PUT(PARSER_REF_SYM, savetok, savelineno, sp); } /* save lastest token */ strlimcpy(savetok, token, sizeof(savetok)); savelineno = lineno; } } else if (c == ',' || c == ';') { if (savetok[0]) { PUT(PARSER_DEF, savetok, lineno, sp); savetok[0] = 0; } } if (level == typedef_savelevel && c == ';') break; } if (param->flags & PARSER_WARNING) { if (c == EOF) warning("unexpected eof. [+%d %s]", lineno, curfile); else if (level != typedef_savelevel) warning("unmatched () block. (last at level %d.)[+%d %s]", level, lineno, curfile); } } break; case C___ATTRIBUTE__: process_attribute(param); break; default: break; } } strbuf_close(sb); if (param->flags & PARSER_WARNING) { if (level != 0) warning("unmatched {} block. (last at level %d.)[+%d %s]", level, lineno, curfile); if (piflevel != 0) warning("unmatched #if block. (last at level %d.)[+%d %s]", piflevel, lineno, curfile); } closetoken(); }
int enumerator_list(void) { if( enumerator_list() ) { match(COMMA); } enumerator(); }
/* * Cpp: read C++ file and pickup tag entries. */ void Cpp(const struct parser_param *param) { int c, cc; int savelevel; int startclass, startthrow, startmacro, startsharp, startequal; char classname[MAXTOKEN]; char completename[MAXCOMPLETENAME]; int classlevel; struct { char *classname; char *terminate; int level; } stack[MAXCLASSSTACK]; const char *interested = "{}=;~"; STRBUF *sb = strbuf_open(0); *classname = *completename = 0; stack[0].classname = completename; stack[0].terminate = completename; stack[0].level = 0; level = classlevel = piflevel = namespacelevel = 0; savelevel = -1; startclass = startthrow = startmacro = startsharp = startequal = 0; if (!opentoken(param->file)) die("'%s' cannot open.", param->file); cmode = 1; /* allow token like '#xxx' */ crflag = 1; /* require '\n' as a token */ cppmode = 1; /* treat '::' as a token */ while ((cc = nexttoken(interested, cpp_reserved_word)) != EOF) { if (cc == '~' && level == stack[classlevel].level) continue; switch (cc) { case SYMBOL: /* symbol */ if (startclass || startthrow) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (peekc(0) == '('/* ) */) { if (param->isnotfunction(token)) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (level > stack[classlevel].level || startequal || startmacro) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (level == stack[classlevel].level && !startmacro && !startsharp && !startequal) { char savetok[MAXTOKEN], *saveline; int savelineno = lineno; strlimcpy(savetok, token, sizeof(savetok)); strbuf_reset(sb); strbuf_puts(sb, sp); saveline = strbuf_value(sb); if (function_definition(param)) { /* ignore constructor */ if (strcmp(stack[classlevel].classname, savetok)) PUT(PARSER_DEF, savetok, savelineno, saveline); } else { PUT(PARSER_REF_SYM, savetok, savelineno, saveline); } } } else { PUT(PARSER_REF_SYM, token, lineno, sp); } break; case CPP_USING: /* * using namespace name; * using ...; */ if ((c = nexttoken(interested, cpp_reserved_word)) == CPP_NAMESPACE) { if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } else { if (param->flags & PARSER_WARNING) warning("missing namespace name. [+%d %s].", lineno, curfile); pushbacktoken(); } } else pushbacktoken(); break; case CPP_NAMESPACE: crflag = 0; /* * namespace name = ...; * namespace [name] { ... } */ if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) { PUT(PARSER_DEF, token, lineno, sp); if ((c = nexttoken(interested, cpp_reserved_word)) == '=') { crflag = 1; break; } } /* * Namespace block doesn't have any influence on level. */ if (c == '{') /* } */ { namespacelevel++; } else { if (param->flags & PARSER_WARNING) warning("missing namespace block. [+%d %s](0x%x).", lineno, curfile, c); } crflag = 1; break; case CPP_EXTERN: /* for 'extern "C"/"C++"' */ if (peekc(0) != '"') /* " */ continue; /* If does not start with '"', continue. */ while ((c = nexttoken(interested, cpp_reserved_word)) == '\n') ; /* * 'extern "C"/"C++"' block is a kind of namespace block. * (It doesn't have any influence on level.) */ if (c == '{') /* } */ namespacelevel++; else pushbacktoken(); break; case CPP_CLASS: DBG_PRINT(level, "class"); if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) { strlimcpy(classname, token, sizeof(classname)); /* * Ignore forward definitions. * "class name;" */ if (peekc(0) != ';') { startclass = 1; PUT(PARSER_DEF, token, lineno, sp); } } break; case '{': /* } */ DBG_PRINT(level, "{"); /* } */ ++level; if ((param->flags & PARSER_BEGIN_BLOCK) && atfirst) { if ((param->flags & PARSER_WARNING) && level != 1) warning("forced level 1 block start by '{' at column 0 [+%d %s].", lineno, curfile); /* } */ level = 1; } if (startclass) { char *p = stack[classlevel].terminate; char *q = classname; if (++classlevel >= MAXCLASSSTACK) die("class stack over flow.[%s]", curfile); if (classlevel > 1) *p++ = '.'; stack[classlevel].classname = p; while (*q) *p++ = *q++; stack[classlevel].terminate = p; stack[classlevel].level = level; *p++ = 0; } startclass = startthrow = 0; break; /* { */ case '}': if (--level < 0) { if (namespacelevel > 0) namespacelevel--; else if (param->flags & PARSER_WARNING) warning("missing left '{' [+%d %s].", lineno, curfile); /* } */ level = 0; } if ((param->flags & PARSER_END_BLOCK) && atfirst) { if ((param->flags & PARSER_WARNING) && level != 0) /* { */ warning("forced level 0 block end by '}' at column 0 [+%d %s].", lineno, curfile); level = 0; } if (level < stack[classlevel].level) *(stack[--classlevel].terminate) = 0; /* { */ DBG_PRINT(level, "}"); break; case '=': /* dirty hack. Don't mimic this. */ if (peekc(0) == '=') { throwaway_nextchar(); } else { startequal = 1; } break; case ';': startthrow = startequal = 0; break; case '\n': if (startmacro && level != savelevel) { if (param->flags & PARSER_WARNING) warning("different level before and after #define macro. reseted. [+%d %s].", lineno, curfile); level = savelevel; } startmacro = startsharp = 0; break; /* * #xxx */ case SHARP_DEFINE: case SHARP_UNDEF: startmacro = 1; savelevel = level; if ((c = nexttoken(interested, cpp_reserved_word)) != SYMBOL) { pushbacktoken(); break; } if (peekc(1) == '('/* ) */) { PUT(PARSER_DEF, token, lineno, sp); while ((c = nexttoken("()", cpp_reserved_word)) != EOF && c != '\n' && c != /* ( */ ')') if (c == SYMBOL) PUT(PARSER_REF_SYM, token, lineno, sp); if (c == '\n') pushbacktoken(); } else { PUT(PARSER_DEF, token, lineno, sp); } break; case SHARP_IMPORT: case SHARP_INCLUDE: case SHARP_INCLUDE_NEXT: case SHARP_ERROR: case SHARP_LINE: case SHARP_PRAGMA: case SHARP_WARNING: case SHARP_IDENT: case SHARP_SCCS: while ((c = nexttoken(interested, cpp_reserved_word)) != EOF && c != '\n') ; break; case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, cc); break; case SHARP_SHARP: /* ## */ (void)nexttoken(interested, cpp_reserved_word); break; case CPP_NEW: if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) PUT(PARSER_REF_SYM, token, lineno, sp); break; case CPP_STRUCT: case CPP_ENUM: case CPP_UNION: c = nexttoken(interested, cpp_reserved_word); if (c == SYMBOL) { if (peekc(0) == '{') /* } */ { PUT(PARSER_DEF, token, lineno, sp); } else { PUT(PARSER_REF_SYM, token, lineno, sp); } c = nexttoken(interested, cpp_reserved_word); } if (c == '{' /* } */ && cc == CPP_ENUM) { enumerator_list(param); } else { pushbacktoken(); } break; case CPP_TEMPLATE: { int level = 0; while ((c = nexttoken("<>", cpp_reserved_word)) != EOF) { if (c == '<') ++level; else if (c == '>') { if (--level == 0) break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } } if (c == EOF && (param->flags & PARSER_WARNING)) warning("template <...> isn't closed. [+%d %s].", lineno, curfile); } break; case CPP_OPERATOR: while ((c = nexttoken(";{", /* } */ cpp_reserved_word)) != EOF) { if (c == '{') /* } */ { pushbacktoken(); break; } else if (c == ';') { break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } } if (c == EOF && (param->flags & PARSER_WARNING)) warning("'{' doesn't exist after 'operator'. [+%d %s].", lineno, curfile); /* } */ break; /* control statement check */ case CPP_THROW: startthrow = 1; case CPP_BREAK: case CPP_CASE: case CPP_CATCH: case CPP_CONTINUE: case CPP_DEFAULT: case CPP_DELETE: case CPP_DO: case CPP_ELSE: case CPP_FOR: case CPP_GOTO: case CPP_IF: case CPP_RETURN: case CPP_SWITCH: case CPP_TRY: case CPP_WHILE: if ((param->flags & PARSER_WARNING) && !startmacro && level == 0) warning("Out of function. %8s [+%d %s]", token, lineno, curfile); break; case CPP_TYPEDEF: { /* * This parser is too complex to maintain. * We should rewrite the whole. */ char savetok[MAXTOKEN]; int savelineno = 0; int typedef_savelevel = level; savetok[0] = 0; /* skip CV qualifiers */ do { c = nexttoken("{}(),;", cpp_reserved_word); } while (IS_CV_QUALIFIER(c) || c == '\n'); if ((param->flags & PARSER_WARNING) && c == EOF) { warning("unexpected eof. [+%d %s]", lineno, curfile); break; } else if (c == CPP_ENUM || c == CPP_STRUCT || c == CPP_UNION) { char *interest_enum = "{},;"; int c_ = c; c = nexttoken(interest_enum, cpp_reserved_word); /* read tag name if exist */ if (c == SYMBOL) { if (peekc(0) == '{') /* } */ { PUT(PARSER_DEF, token, lineno, sp); } else { PUT(PARSER_REF_SYM, token, lineno, sp); } c = nexttoken(interest_enum, cpp_reserved_word); } if (c_ == CPP_ENUM) { if (c == '{') /* } */ c = enumerator_list(param); else pushbacktoken(); } else { for (; c != EOF; c = nexttoken(interest_enum, cpp_reserved_word)) { switch (c) { case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, c); continue; default: break; } if (c == ';' && level == typedef_savelevel) { if (savetok[0]) PUT(PARSER_DEF, savetok, savelineno, sp); break; } else if (c == '{') level++; else if (c == '}') { if (--level == typedef_savelevel) break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); /* save lastest token */ strlimcpy(savetok, token, sizeof(savetok)); savelineno = lineno; } } if (c == ';') break; } if ((param->flags & PARSER_WARNING) && c == EOF) { warning("unexpected eof. [+%d %s]", lineno, curfile); break; } } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } savetok[0] = 0; while ((c = nexttoken("(),;", cpp_reserved_word)) != EOF) { switch (c) { case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, c); continue; default: break; } if (c == '(') level++; else if (c == ')') level--; else if (c == SYMBOL) { if (level > typedef_savelevel) { PUT(PARSER_REF_SYM, token, lineno, sp); } else { /* put latest token if any */ if (savetok[0]) { PUT(PARSER_REF_SYM, savetok, savelineno, sp); } /* save lastest token */ strlimcpy(savetok, token, sizeof(savetok)); savelineno = lineno; } } else if (c == ',' || c == ';') { if (savetok[0]) { PUT(PARSER_DEF, savetok, lineno, sp); savetok[0] = 0; } } if (level == typedef_savelevel && c == ';') break; } if (param->flags & PARSER_WARNING) { if (c == EOF) warning("unexpected eof. [+%d %s]", lineno, curfile); else if (level != typedef_savelevel) warning("unmatched () block. (last at level %d.)[+%d %s]", level, lineno, curfile); } } break; case CPP___ATTRIBUTE__: process_attribute(param); break; default: break; } } strbuf_close(sb); if (param->flags & PARSER_WARNING) { if (level != 0) warning("unmatched {} block. (last at level %d.)[+%d %s]", level, lineno, curfile); if (piflevel != 0) warning("unmatched #if block. (last at level %d.)[+%d %s]", piflevel, lineno, curfile); } closetoken(); }
static void member_declaration_list(struct typetree *type) { struct namespace ns = {0}; struct typetree *decl_base, *decl_type; const char *name; push_scope(&ns); do { decl_base = declaration_specifiers(NULL); do { name = NULL; decl_type = declarator(decl_base, &name); if (!name) { error("Missing name in member declarator."); exit(1); } else if (!size_of(decl_type)) { error("Field '%s' has incomplete type '%t'.", name, decl_type); exit(1); } else { sym_add(&ns, name, decl_type, SYM_DECLARATION, LINK_NONE); type_add_member(type, name, decl_type); } if (peek().token == ',') { consume(','); continue; } } while (peek().token != ';'); consume(';'); } while (peek().token != '}'); pop_scope(&ns); } static struct typetree *struct_or_union_declaration(void) { struct symbol *sym = NULL; struct typetree *type = NULL; enum type kind = (next().token == STRUCT) ? T_STRUCT : T_UNION; if (peek().token == IDENTIFIER) { const char *name = consume(IDENTIFIER).strval; sym = sym_lookup(&ns_tag, name); if (!sym) { type = type_init(kind); sym = sym_add(&ns_tag, name, type, SYM_TYPEDEF, LINK_NONE); } else if (is_integer(&sym->type)) { error("Tag '%s' was previously declared as enum.", sym->name); exit(1); } else if (sym->type.type != kind) { error("Tag '%s' was previously declared as %s.", sym->name, (sym->type.type == T_STRUCT) ? "struct" : "union"); exit(1); } /* Retrieve type from existing symbol, possibly providing a complete * definition that will be available for later declarations. Overwrites * existing type information from symbol table. */ type = &sym->type; if (peek().token == '{' && type->size) { error("Redefiniton of '%s'.", sym->name); exit(1); } } if (peek().token == '{') { if (!type) { /* Anonymous structure; allocate a new standalone type, * not part of any symbol. */ type = type_init(kind); } consume('{'); member_declaration_list(type); assert(type->size); consume('}'); } /* Return to the caller a copy of the root node, which can be overwritten * with new type qualifiers without altering the tag registration. */ return (sym) ? type_tagged_copy(&sym->type, sym->name) : type; } static void enumerator_list(void) { struct var val; struct symbol *sym; int enum_value = 0; consume('{'); do { const char *name = consume(IDENTIFIER).strval; if (peek().token == '=') { consume('='); val = constant_expression(); if (!is_integer(val.type)) { error("Implicit conversion from non-integer type in enum."); } enum_value = val.imm.i; } sym = sym_add( &ns_ident, name, &basic_type__int, SYM_ENUM_VALUE, LINK_NONE); sym->enum_value = enum_value++; if (peek().token != ',') break; consume(','); } while (peek().token != '}'); consume('}'); } static struct typetree *enum_declaration(void) { struct typetree *type = type_init(T_SIGNED, 4); consume(ENUM); if (peek().token == IDENTIFIER) { struct symbol *tag = NULL; const char *name = consume(IDENTIFIER).strval; tag = sym_lookup(&ns_tag, name); if (!tag || tag->depth < ns_tag.current_depth) { tag = sym_add(&ns_tag, name, type, SYM_TYPEDEF, LINK_NONE); } else if (!is_integer(&tag->type)) { error("Tag '%s' was previously defined as aggregate type.", tag->name); exit(1); } /* Use enum_value as a sentinel to represent definition, checked on * lookup to detect duplicate definitions. */ if (peek().token == '{') { if (tag->enum_value) { error("Redefiniton of enum '%s'.", tag->name); } enumerator_list(); tag->enum_value = 1; } } else { enumerator_list(); } /* Result is always integer. Do not care about the actual enum definition, * all enums are ints and no type checking is done. */ return type; } static struct typetree get_basic_type_from_specifier(unsigned short spec) { switch (spec) { case 0x0001: /* void */ return basic_type__void; case 0x0002: /* char */ case 0x0012: /* signed char */ return basic_type__char; case 0x0022: /* unsigned char */ return basic_type__unsigned_char; case 0x0004: /* short */ case 0x0014: /* signed short */ case 0x000C: /* short int */ case 0x001C: /* signed short int */ return basic_type__short; case 0x0024: /* unsigned short */ case 0x002C: /* unsigned short int */ return basic_type__unsigned_short; case 0x0008: /* int */ case 0x0010: /* signed */ case 0x0018: /* signed int */ return basic_type__int; case 0x0020: /* unsigned */ case 0x0028: /* unsigned int */ return basic_type__unsigned_int; case 0x0040: /* long */ case 0x0050: /* signed long */ case 0x0048: /* long int */ case 0x0058: /* signed long int */ case 0x00C0: /* long long */ case 0x00D0: /* signed long long */ case 0x00D8: /* signed long long int */ return basic_type__long; case 0x0060: /* unsigned long */ case 0x0068: /* unsigned long int */ case 0x00E0: /* unsigned long long */ case 0x00E8: /* unsigned long long int */ return basic_type__unsigned_long; case 0x0100: /* float */ return basic_type__float; case 0x0200: /* double */ case 0x0240: /* long double */ return basic_type__double; default: error("Invalid type specification."); exit(1); } } /* Parse type, qualifiers and storage class. Do not assume int by default, but * require at least one type specifier. Storage class is returned as token * value, unless the provided pointer is NULL, in which case the input is parsed * as specifier-qualifier-list. */ struct typetree *declaration_specifiers(int *stc) { struct typetree *type = NULL; struct token tok; int done = 0; /* Use a compact bit representation to hold state about declaration * specifiers. Initialize storage class to sentinel value. */ unsigned short spec = 0x0000; enum qualifier qual = Q_NONE; if (stc) *stc = '$'; #define set_specifier(d) \ if (spec & d) error("Duplicate type specifier '%s'.", tok.strval); \ next(); spec |= d; #define set_qualifier(d) \ if (qual & d) error("Duplicate type qualifier '%s'.", tok.strval); \ next(); qual |= d; #define set_storage_class(t) \ if (!stc) error("Unexpected storage class in qualifier list."); \ else if (*stc != '$') error("Multiple storage class specifiers."); \ next(); *stc = t; do { switch ((tok = peek()).token) { case VOID: set_specifier(0x001); break; case CHAR: set_specifier(0x002); break; case SHORT: set_specifier(0x004); break; case INT: set_specifier(0x008); break; case SIGNED: set_specifier(0x010); break; case UNSIGNED: set_specifier(0x020); break; case LONG: if (spec & 0x040) { set_specifier(0x080); } else { set_specifier(0x040); } break; case FLOAT: set_specifier(0x100); break; case DOUBLE: set_specifier(0x200); break; case CONST: set_qualifier(Q_CONST); break; case VOLATILE: set_qualifier(Q_VOLATILE); break; case IDENTIFIER: { struct symbol *tag = sym_lookup(&ns_ident, tok.strval); if (tag && tag->symtype == SYM_TYPEDEF && !type) { consume(IDENTIFIER); type = type_init(T_STRUCT); *type = tag->type; } else { done = 1; } break; } case UNION: case STRUCT: if (!type) { type = struct_or_union_declaration(); } else { done = 1; } break; case ENUM: if (!type) { type = enum_declaration(); } else { done = 1; } break; case AUTO: case REGISTER: case STATIC: case EXTERN: case TYPEDEF: set_storage_class(tok.token); break; default: done = 1; break; } if (type && spec) { error("Invalid combination of declaration specifiers."); exit(1); } } while (!done); #undef set_specifier #undef set_qualifier #undef set_storage_class if (type) { if (qual & type->qualifier) { error("Duplicate type qualifier:%s%s.", (qual & Q_CONST) ? " const" : "", (qual & Q_VOLATILE) ? " volatile" : ""); } } else if (spec) { type = type_init(T_STRUCT); *type = get_basic_type_from_specifier(spec); } else { error("Missing type specifier."); exit(1); } type->qualifier |= qual; return type; } /* Set var = 0, using simple assignment on members for composite types. This * rule does not consume any input, but generates a series of assignments on the * given variable. Point is to be able to zero initialize using normal simple * assignment rules, although IR can become verbose for large structures. */ static void zero_initialize(struct block *block, struct var target) { int i; struct var var; assert(target.kind == DIRECT); switch (target.type->type) { case T_STRUCT: case T_UNION: target.type = unwrapped(target.type); var = target; for (i = 0; i < nmembers(var.type); ++i) { target.type = get_member(var.type, i)->type; target.offset = var.offset + get_member(var.type, i)->offset; zero_initialize(block, target); } break; case T_ARRAY: assert(target.type->size); var = target; target.type = target.type->next; assert(is_struct(target.type) || !target.type->next); for (i = 0; i < var.type->size / var.type->next->size; ++i) { target.offset = var.offset + i * var.type->next->size; zero_initialize(block, target); } break; case T_POINTER: var = var_zero(8); var.type = type_init(T_POINTER, &basic_type__void); eval_assign(block, target, var); break; case T_UNSIGNED: case T_SIGNED: var = var_zero(target.type->size); eval_assign(block, target, var); break; default: error("Invalid type to zero-initialize, was '%t'.", target.type); exit(1); } }