%{ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "parseproto.h" #include static decl_spec_t *declspec_Construct(void); static void declspec_Destroy(decl_spec_t *); static decl_spec_t *declspec_Init(stt_t, char *); static char *declspec_VerifySTT(stt_t, stt_t); static decl_spec_t *declspec_AddSTT(decl_spec_t *, stt_t, const char **); static decl_spec_t *declspec_AddDS(decl_spec_t *, decl_spec_t *, const char **); static stt_t declspec_GetSTT(decl_spec_t *); static char *declspec_GetTag(decl_spec_t *); static type_t *type_Construct(void); static void type_Destroy(type_t *); static type_t *type_SetPtr(type_t *, stt_t); static type_t *type_SetFun(type_t *, decl_t *); static type_t *type_AddTail(type_t *, type_t *); static const char *type_Verify(type_t *); static decl_t *decl_Construct(void); static decl_t *decl_AddArg(decl_t *, decl_t *); static int decl_IsVoid(decl_t *); static int decl_IsVoidArray(decl_t *); static const char *decl_VerifyArgs(decl_t *); static decl_t *decl_AddDS(decl_t *, decl_spec_t *, const char **); static decl_t *decl_AddTypeTail(decl_t *, type_t *); static decl_t *decl_addptr(decl_t *, type_t *); static decl_t *decl_addary(decl_t *, char *); static decl_t *decl_addfun(decl_t *, decl_t *); static decl_t *decl_addellipsis(decl_t *); #if defined(DEBUG) static void type_PrintType(type_t *, int); static void decl_PrintDecl(decl_t *, int); static void decl_PrintTraceInfo(decl_t *); static char *de_const(char *); #endif static int yylex(void); static void yyerror(const char *); static int yyparse(void); #if defined(MEM_DEBUG) static int declspec_Construct_calls; static int type_Construct_calls; static int decl_Construct_calls; #endif #if defined(DEBUG) static char *de_const(char *); #endif %} %union { char *s_val; int i_val; stt_t stt_val; decl_spec_t *ds_val; type_t *t_val; decl_t *d_val; } %token ELLIPSIS %token INTEGER %token IDENTIFIER %token TYPEDEF_NAME %type constant_expression %token REGISTER %token TYPEDEF EXTERN AUTO STATIC %token VOID CHAR SHORT INT LONG %token FLOAT DOUBLE SIGNED UNSIGNED %token CONST VOLATILE RESTRICT RESTRICT_KYWD %type struct_or_union %type storage_class_specifier %type type_qualifier %type type_qualifier_list %token STRUCT UNION %token ENUM %type declaration_specifiers %type type_specifier %type struct_or_union_specifier enum_specifier %type typedef_name %type pointer %type declaration %type init_declarator_list init_declarator %type declarator %type direct_declarator %type parameter_type_list parameter_list %type parameter_declaration %type abstract_declarator %type direct_abstract_declarator %start declaration %% /* * The grammar is derived from ANSI/ISO 9899-1990. */ declaration : declaration_specifiers init_declarator_list ';' { decl_t *dp; protop = $$ = $2; /* only one declaration allowed */ assert(protop->d_next == NULL); for (dp = $2; dp && (errstr == NULL); dp = dp->d_next) { const char *sp; decl_AddDS(dp, $1, &errstr); if (sp = decl_Verify(dp)) errstr = sp; } declspec_Destroy($1); } | error ';' { protop = $$ = NULL; errstr = "function prototype syntax error"; } /* * XXX - Does not support a "stand-alone" declaration specifier. It is * essentially a type declaration, for example: * * typedef enum { FALSE = 0, TRUE = 1 } boolean_t; * or * struct _name { char *first; char *last }; */ /* XXX | declaration_specifiers */ ; declaration_specifiers : storage_class_specifier declaration_specifiers { char const *ep; $$ = declspec_AddDS($2, $1, &ep); declspec_Destroy($1); if (errstr == NULL) errstr = ep; } | storage_class_specifier | type_specifier declaration_specifiers { const char *ep; $$ = declspec_AddDS($2, $1, &ep); declspec_Destroy($1); if (errstr == NULL) errstr = ep; } | type_specifier | type_qualifier declaration_specifiers { const char *ep; $$ = declspec_AddDS($2, $1, &ep); declspec_Destroy($1); if (errstr == NULL) errstr = ep; } | type_qualifier ; storage_class_specifier : REGISTER { $$ = declspec_Init(SCS_REGISTER, NULL); } /* * XXX - Does not support any storage class specifier other than * register, and then only for function arguments. * | TYPEDEF { $$ = declspec_Init(SCS_TYPEDEF, NULL); } | EXTERN { $$ = declspec_Init(SCS_EXTERN, NULL); } | STATIC { $$ = declspec_Init(SCS_STATIC, NULL); } | AUTO { $$ = declspec_Init(SCS_AUTO, NULL); } */ ; type_specifier : VOID { $$ = declspec_Init(TS_VOID, NULL); atIDENT = 1; } | CHAR { $$ = declspec_Init(TS_CHAR, NULL); atIDENT = 1; } | SHORT { $$ = declspec_Init(TS_SHORT, NULL); atIDENT = 1; } | INT { $$ = declspec_Init(TS_INT, NULL); atIDENT = 1; } | LONG { $$ = declspec_Init(TS_LONG, NULL); atIDENT = 1; } | FLOAT { $$ = declspec_Init(TS_FLOAT, NULL); atIDENT = 1; } | DOUBLE { $$ = declspec_Init(TS_DOUBLE, NULL); atIDENT = 1; } | SIGNED { $$ = declspec_Init(TS_SIGNED, NULL); atIDENT = 1; } | UNSIGNED { $$ = declspec_Init(TS_UNSIGNED, NULL); atIDENT = 1; } | struct_or_union_specifier | enum_specifier | typedef_name ; typedef_name : TYPEDEF_NAME { $$ = declspec_Init(TS_TYPEDEF, $1); atIDENT = 1; free($1); } ; /* * The "restrict" keyword is new in the C99 standard. * It is type qualifier like const and volatile. * We are using "_RESTRICT_KYWD" in headers and source code so * it is easily turned on and off by various macros at compile time. * In order for the "restrict" keyword to be recognized you must * be using a C99 compliant compiler in its native mode. */ type_qualifier : CONST { $$ = declspec_Init(TQ_CONST, NULL); } | VOLATILE { $$ = declspec_Init(TQ_VOLATILE, NULL); } | RESTRICT { $$ = declspec_Init(TQ_RESTRICT, NULL); } | RESTRICT_KYWD { $$ = declspec_Init(TQ_RESTRICT_KYWD, NULL); } ; struct_or_union_specifier : struct_or_union { atIDENT = 1; } IDENTIFIER { $$ = declspec_Init($1, $3); free($3); } /* * XXX - struct or union definitions are not supported. It is generally * not done within the context of a function declaration (prototype) or * variable definition. | struct_or_union IDENTIFIER '{' struct_declaration_list '}' | struct_or_union '{' struct_declaration_list '}' */ ; struct_or_union : STRUCT { $$ = TS_STRUCT; } | UNION { $$ = TS_UNION; } ; init_declarator_list : init_declarator { $$ = $1; atIDENT = 1; } /* * XXX - Does not support a comma separated list of declarations or * definitions. Function prototypes or variable definitions must be * given as one per C statement. | init_declarator_list ',' init_declarator { $$ = decl_AddArg($1, $3); atIDENT = 1; } */ ; init_declarator : declarator /* * XXX - Initialization is not supported. | declarator '=' initializer */ ; enum_specifier : ENUM { atIDENT = 1; } IDENTIFIER { $$ = declspec_Init(TS_ENUM, $3); free($3); } /* * XXX - enumerator definition is not supported for the same reasons * struct|union definition is not supported. | ENUM IDENTIFIER '{' enumerator_list '}' | ENUM '{' enumerator_list '}' */ ; declarator : pointer direct_declarator { $$ = decl_addptr($2, $1); } | direct_declarator ; direct_declarator : IDENTIFIER { $$ = decl_SetName(decl_Construct(), $1); atIDENT = 0; free($1); } | '(' declarator ')' { $$ = $2; } | direct_declarator '[' constant_expression ']' { $$ = decl_addary($1, $3); free($3); } | direct_declarator '[' ']' { $$ = decl_addary($1, NULL); } | direct_declarator '(' parameter_type_list ')' { $$ = decl_addfun($1, $3); } | direct_declarator '(' ')' { $$ = decl_addfun($1, NULL); } ; pointer : '*' type_qualifier_list { $$ = type_SetPtr(type_Construct(), ($2)->ds_stt); declspec_Destroy($2); } | '*' { $$ = type_SetPtr(type_Construct(), TQ_NONE); } | '*' type_qualifier_list pointer { type_t *tp = type_Construct(); type_SetPtr(tp, ($2)->ds_stt); declspec_Destroy($2); $$ = type_AddTail($3, tp); } | '*' pointer { type_t *tp = type_Construct(); type_SetPtr(tp, TQ_NONE); $$ = type_AddTail($2, tp); } ; type_qualifier_list : type_qualifier | type_qualifier_list type_qualifier { const char *ep; /* XXX - ignore any error */ $$ = declspec_AddDS($1, $2, &ep); declspec_Destroy($2); } ; parameter_type_list : parameter_list | parameter_list ',' ELLIPSIS { $$ = decl_addellipsis($1); } ; parameter_list : parameter_declaration { const char *sp = type_Verify($1->d_type); if (sp) errstr = sp; $$ = $1; atIDENT = 0; } | parameter_list ',' parameter_declaration { const char *sp = type_Verify($3->d_type); if (sp) errstr = sp; $$ = decl_AddArg($1, $3); atIDENT = 0; } ; parameter_declaration : declaration_specifiers declarator { const char *ep; $$ = decl_AddDS($2, $1, &ep); declspec_Destroy($1); if (errstr == NULL) errstr = ep; } | declaration_specifiers abstract_declarator { const char *ep; $$ = decl_AddDS($2, $1, &ep); declspec_Destroy($1); if (errstr == NULL) errstr = ep; } | declaration_specifiers { const char *ep; $$ = decl_AddDS(decl_Construct(), $1, &ep); declspec_Destroy($1); if (errstr == NULL) errstr = ep; } ; abstract_declarator : pointer { $$ = decl_addptr(decl_Construct(), $1); } | pointer direct_abstract_declarator { $$ = decl_addptr($2, $1); } | direct_abstract_declarator ; direct_abstract_declarator : '(' abstract_declarator ')' { $$ = $2; } | direct_abstract_declarator '[' constant_expression ']' { $$ = decl_addary($1, $3); free($3); } | '[' constant_expression ']' { $$ = decl_addary(decl_Construct(), $2); free($2); } | direct_abstract_declarator '[' ']' { $$ = decl_addary($1, NULL); } | '[' ']' { $$ = decl_addary(decl_Construct(), NULL); } | direct_abstract_declarator '(' parameter_type_list ')' { $$ = decl_addfun($1, $3); } | '(' parameter_type_list ')' { $$ = decl_addfun(decl_Construct(), $2); } | direct_abstract_declarator '(' ')' { $$ = decl_addfun($1, NULL); } | '(' ')' { $$ = decl_addfun(decl_Construct(), NULL); } ; /* * XXX - General case constant expressions are not supported. It would * be easy to implement (for the most part), but there are no cases to * date that require such a facility. The grammar does allow an * identifier (or typedef name) to be used since the prototype is not * processed by CPP. The only integer constant that is supported is * decimal. */ constant_expression : INTEGER | IDENTIFIER | TYPEDEF_NAME ; %% /* Data Declarations */ typedef struct { char *name; int token; stt_t stt; } keyword_t; typedef struct { stt_t s_stt; char *s_str; } sttpair_t; /* External Declarations */ static const keyword_t *lookup_keyword(const char *); static const char *lookup_sttpair(stt_t); static int getch(void); static void ungetch(int); static void skipwhitespace(void); static int lookahead(int); static void skipcomment(void); /* External Definitions */ static char *input = NULL; /* current place in the input stream */ /* at point in stream were identifier is expected */ static int atIDENT = 0; static decl_t *protop = NULL; /* pointer to prototype */ static const char *errstr = NULL; /* error message */ /* * lookup_keyword - Given a string, return the keyword_t or NULL. */ static const keyword_t * lookup_keyword(const char *name) { static const keyword_t keytbl[] = { { "register", REGISTER, SCS_REGISTER }, #if UNSUPPORTED { "typedef", TYPEDEF, SCS_TYPEDEF }, { "auto", AUTO, SCS_AUTO }, { "static", STATIC, SCS_STATIC }, { "extern", EXTERN, SCS_EXTERN }, #endif /* UNSUPPORTED */ { "void", VOID, TS_VOID }, { "char", CHAR, TS_CHAR }, { "short", SHORT, TS_SHORT }, { "int", INT, TS_INT }, { "long", LONG, TS_LONG }, { "float", FLOAT, TS_FLOAT }, { "double", DOUBLE, TS_DOUBLE }, { "signed", SIGNED, TS_SIGNED }, { "unsigned", UNSIGNED, TS_UNSIGNED }, { "struct", STRUCT, TS_STRUCT }, { "union", UNION, TS_UNION }, { "enum", ENUM, TS_ENUM }, { "const", CONST, TQ_CONST }, { "volatile", VOLATILE, TQ_VOLATILE }, { "restrict", RESTRICT, TQ_RESTRICT }, { "_RESTRICT_KYWD",RESTRICT_KYWD, TQ_RESTRICT_KYWD}, }; #define NKEYWORD (sizeof (keytbl)/sizeof (keyword_t)) int i; for (i = 0; i < NKEYWORD; ++i) { char *s = keytbl[i].name; if ((*s == *name) && (strcmp(s, name) == 0)) return (&keytbl[i]); } return (NULL); } /* * lookup_sttpair - Given an stt_t return a string or NULL. * */ static const char * lookup_sttpair(stt_t s) { /* valid type specifier combinations */ static const sttpair_t stttbl[] = { { TS_VOID, "void" }, { TS_CHAR, "char" }, { TS_SIGNED | TS_CHAR, "signed char" }, { TS_UNSIGNED | TS_CHAR, "unsigned char" }, { TS_SHORT, "short" }, { TS_SIGNED | TS_SHORT, "signed short" }, { TS_SHORT | TS_INT, "short int" }, { TS_SIGNED | TS_SHORT | TS_INT, "signed short int" }, { TS_UNSIGNED | TS_SHORT, "unsigned short" }, { TS_UNSIGNED | TS_SHORT | TS_INT, "unsigned short int" }, { TS_INT, "int" }, { TS_SIGNED, "signed" }, { TS_SIGNED | TS_INT, "signed int" }, { TS_NO_TS, "" }, { TS_UNSIGNED, "unsigned" }, { TS_UNSIGNED | TS_INT, "unsigned int" }, { TS_LONG, "long" }, { TS_SIGNED | TS_LONG, "signed long" }, { TS_LONG | TS_INT, "long int" }, { TS_SIGNED | TS_LONG | TS_INT, "signed long int" }, { TS_UNSIGNED | TS_LONG, "unsigned long" }, { TS_UNSIGNED | TS_LONG | TS_INT, "unsigned long int" }, { TS_FLOAT, "float" }, { TS_DOUBLE, "double" }, { TS_LONG | TS_DOUBLE, "long double" }, { TS_STRUCT, "struct" }, { TS_UNION, "union" }, { TS_ENUM, "enum" }, { TS_TYPEDEF, "" }, /* non-ANSI type: long long */ { TS_LONGLONG, "long long" }, { TS_LONGLONG | TS_INT, "long long int" }, { TS_SIGNED | TS_LONGLONG, "signed long long" }, { TS_UNSIGNED | TS_LONGLONG, "unsigned long long" }, { TS_SIGNED | TS_LONGLONG | TS_INT, "signed long long int" }, { TS_UNSIGNED | TS_LONGLONG | TS_INT, "unsigned long long int" }, }; #define NDECLSPEC (sizeof (stttbl)/sizeof (sttpair_t)) int i; for (i = 0; i < NDECLSPEC; ++i) if (s == stttbl[i].s_stt) return (stttbl[i].s_str); return (NULL); } /* * yylex - return next token from the the input stream. * * The lexical analyzer does not recognize all possible C lexical * elements. It only recognizes those associated with function * declarations (read: prototypes) and data definitions. */ static int yylex(void) { char buf[BUFSIZ]; /* string version of token */ int c; int i = 0; restart: skipwhitespace(); switch (c = getch()) { case '/': if (lookahead('*')) { skipcomment(); goto restart; } return (c); case '.': if (lookahead('.')) { if (lookahead('.')) return (ELLIPSIS); } return (c); case EOF: case '(': case ')': case ',': case '[': case ']': case ';': case '*': return (c); default: if ((c == '_') || isalpha(c)) { const keyword_t *kp; do { buf[i++] = c; c = getch(); } while ((c == '_') || isalnum(c)); ungetch(c); buf[i] = '\0'; if ((kp = lookup_keyword(buf)) != NULL) { yylval.stt_val = kp->stt; return (kp->token); } else { yylval.s_val = strdup(buf); return ((atIDENT) ? IDENTIFIER : TYPEDEF_NAME); } } else if (isdigit(c)) { do { buf[i++] = c; } while (isdigit(c = getch())); ungetch(c); buf[i] = '\0'; yylval.s_val = strdup(buf); return (INTEGER); } else return (c); } /* NOTREACHED */ } /* getch - return the next character from the input stream. */ static int getch(void) { int c; if ((c = *input) == '\0') c = EOF; else /* only advance on non-NULL */ input++; return (c); } /* ungetch - return a character to the input stream. */ static void ungetch(int c) { *(--input) = c; } /* skipwhitespace - skip over whitespace in the input stream. */ static void skipwhitespace(void) { int c; while (isspace(c = getch())) ; ungetch(c); } /* skipcomment - scan ahead to the next end of comment. */ static void skipcomment(void) { loop { int c; switch (c = getch()) { case EOF: return; case '*': if (lookahead('/')) return; } } /* NOTREACHED */ } /* lookahead - does next character match 'c'? */ static int lookahead(int c) { int ch = getch(); int match; if (!(match = (ch == c))) ungetch(ch); return (match); } /* putNtabs - write N '\t' to standard output. */ #if defined(DEBUG) static void putNTabs(int n) { int i; for (i = 0; i < n; ++i) putchar('\t'); } #endif /* DEBUG */ /* D E C L A R A T I O N S P E C I F I E R S */ /* * Declaration specifiers encode storage class, type specifier and type * qualifier information. This includes any identifiers associated with * struct, union or enum declarations. Typedef names are also encoded * in declaration specifiers. */ /* declspec_Construct - allocate and initialize a declspec_t. */ static decl_spec_t * declspec_Construct(void) { decl_spec_t *dsp = malloc(sizeof (decl_spec_t)); assert(dsp != NULL); dsp->ds_stt = SCS_NONE | TS_NO_TS | TQ_NONE; dsp->ds_id = NULL; #if defined(MEM_DEBUG) ++declspec_Construct_calls; #endif return (dsp); } /* declspec_Destroy - free a declspec_t. */ static void declspec_Destroy(decl_spec_t *dsp) { free(dsp->ds_id); free(dsp); #if defined(MEM_DEBUG) --declspec_Construct_calls; #endif } /* * declspec_Init - allocate and initialize a declspec_t given an * stt_t and identifier. * * Note: * 1) identifier can be NULL. * 2) errors resulting in the stt_t and identifier are ignored. */ static decl_spec_t * declspec_Init(stt_t s, char *tagp) { const char *p; decl_spec_t *dsp = declspec_Construct(); decl_spec_t tmp; tmp.ds_stt = s; tmp.ds_id = tagp; declspec_AddDS(dsp, &tmp, &p); /* XXX ignore any error */ return (dsp); } /* * declspec_VerifySTT - verify that the two given stt_t can be combined. * * Note: * 1) The return value is a const char *, non-NULL to indicate an error. */ static char * declspec_VerifySTT(stt_t s1, stt_t s2) { stt_t result; if ((s1 | s2) != (s1 ^ s2)) return ("attempt to add declaration specifier " "that is already present"); result = (s1 | s2) & TS_MASK; if (lookup_sttpair(result) == NULL) { if (STT_isbasic(result) && STT_isderived(result)) return ("attempt to combine basic and " "derived types"); if (STT_isvoid(result) && (STT_isbasic(result) || STT_isderived(result))) return ("attempt to combine void with " "other type specifiers"); if (STT_isfloat(result) && STT_isint(result)) return ("attempt to combine floating and " "integer type specifiers"); if (STT_ischar(result) && STT_isint(result)) return ("attempt to combine character and " "integer type specifiers"); if (STT_has_explicit_sign(result) && (STT_isfloat(result) || STT_isderived(result))) return ("attempt to combine signed or " "unsigned with float or derived type"); return ("invalid declaration specifier"); } return (NULL); } /* * declspec_AddSTT - add an stt_t to a decl_spec_t. * * Note: * 1) The "long long" type is handled here. * If both stt_t include TS_LONG then this is an attempt to use * "long long". The TS_LONG is cleared from the s1 and s2 and * then TS_LONGLONG is added to s2. The resulting s1 and s2 are * passed to declspec_VerifySTT to determine if the result is valid. * * 2) This method of handling "long long" does detect the case of * "long double long" and all it's variant forms. */ static decl_spec_t * declspec_AddSTT(decl_spec_t *dsp, stt_t s2, const char **err) { stt_t s1 = dsp->ds_stt; /* non-ANSI type: long long */ if ((s1 & TS_LONG) && (s2 & TS_LONG)) { s1 &= ~(TS_LONG); dsp->ds_stt = s1; s2 &= ~(TS_LONG); s2 |= TS_LONGLONG; } if ((*err = declspec_VerifySTT(s1, s2)) == NULL) dsp->ds_stt |= s2; return (dsp); } /* * declpec_AddDS - add a decl_spec_t to an existing decl_spec_t. */ static decl_spec_t * declspec_AddDS(decl_spec_t *dsp, decl_spec_t *tsp, const char **err) { declspec_AddSTT(dsp, tsp->ds_stt, err); if ((*err == NULL) && tsp->ds_id) { free(dsp->ds_id); dsp->ds_id = strdup(tsp->ds_id); assert(dsp->ds_id != NULL); } return (dsp); } /* * declspec_GetSTT - return the stt_t within a decl_spec_t. */ static stt_t declspec_GetSTT(decl_spec_t *dsp) { return (dsp->ds_stt); } /* * declspec_GetTag - return the identifier within a decl_spec_t. */ static char * declspec_GetTag(decl_spec_t *dsp) { return (dsp->ds_id); } /* * declspec_ToString - convert a decl_spec_t into a string. * * Note: * 1) The form of the resulting string is always the same, i.e. * * [register] [type_specifier] [const] [volatile] * * dsp must be correct * */ char * declspec_ToString(char *bufp, decl_spec_t *dsp) { const char *s; int something = 0; *bufp = '\0'; /* storage class specifier */ switch (dsp->ds_stt & SCS_MASK) { case SCS_REGISTER: strcat(bufp, "register"); something = 1; break; } s = lookup_sttpair(dsp->ds_stt & TS_MASK); /* type specifier */ switch (dsp->ds_stt & TS_MASK) { case TS_STRUCT: case TS_UNION: case TS_ENUM: if (something) strcat(bufp, " "); strcat(bufp, s); strcat(bufp, " "); strcat(bufp, dsp->ds_id); break; case TS_TYPEDEF: if (something) strcat(bufp, " "); strcat(bufp, dsp->ds_id); break; default: if (something) strcat(bufp, " "); strcat(bufp, s); break; } if (s) something = 1; if (something && (dsp->ds_stt & TQ_MASK)) strcat(bufp, " "); if (dsp->ds_stt & TQ_CONST) /* type qualifier */ strcat(bufp, "const"); if (dsp->ds_stt & TQ_VOLATILE) { if (dsp->ds_stt & TQ_CONST) strcat(bufp, " "); strcat(bufp, "volatile"); } /* * It currently acknowledges and ignores restrict or _RESTRICT_KYWD * in code generation because of the uncertain behavior of "restrict". */ if (dsp->ds_stt & TQ_RESTRICT) strcat(bufp, ""); if (dsp->ds_stt & TQ_RESTRICT_KYWD) strcat(bufp, ""); return (bufp); } /* T Y P E M O D I F I E R S */ /* * Type modifiers encode the "array of...", "pointer to ..." and * "function returning ..." aspects of C types. The modifiers are kept * as a linked list in precedence order. The grammar encodes the * precedence order described by the standard. * * Type modifiers are always added at the end of list and the list is * always traversed from head to tail. */ /* type_Construct - allocate and initialize a type_t. */ static type_t * type_Construct(void) { type_t *tp = malloc(sizeof (type_t)); assert(tp != NULL); tp->t_next = NULL; /* generic */ tp->t_dt = DD_NONE; tp->t_nargs = 0; /* DD_FUN */ tp->t_ellipsis = 0; tp->t_args = NULL; /* DD_PTR */ tp->t_stt = (SCS_NONE | TS_NO_TS | TQ_NONE); tp->t_sizestr = NULL; /* DD_ARY */ #if defined(MEM_DEBUG) ++type_Construct_calls; #endif return (tp); } /* type_Destroy - free a type_t list. */ static void type_Destroy(type_t *tp) { while (tp) { type_t *nextp = tp->t_next; switch (tp->t_dt) { case DD_FUN: decl_Destroy(tp->t_args); break; case DD_PTR: break; case DD_ARY: free(tp->t_sizestr); break; } free(tp); tp = nextp; #if defined(MEM_DEBUG) --type_Construct_calls; #endif } } /* * type_SetPtr - make a type_t into a "pointer to ..." variant. * * Note: * 1) The stt_t will encode any type qualifiers (const, volatile). */ static type_t * type_SetPtr(type_t *tp, stt_t s) { assert(tp->t_dt == DD_NONE); tp->t_dt = DD_PTR; tp->t_stt = s & TQ_MASK; return (tp); } /* * type_SetAry - make a type_t into an "array of ...", variant. * * Note: * 1) The array dimension can be NULL to indicate undefined, i.e. []. */ static type_t * type_SetAry(type_t *tp, char *dim) { assert(tp->t_dt == DD_NONE); assert(tp->t_sizestr == NULL); tp->t_dt = DD_ARY; if (dim) { tp->t_sizestr = strdup(dim); assert(tp->t_sizestr != NULL); } else tp->t_sizestr = NULL; return (tp); } /* * type_SetFun - make a type_t into a "function returning ..." variant. * * Note: * 1) The argument list can be NULL to indicate undefined, i.e. (). */ static type_t * type_SetFun(type_t *tp, decl_t *arglist) { assert(tp->t_dt == DD_NONE); tp->t_dt = DD_FUN; if (arglist) { tp->t_nargs = decl_GetArgLength(arglist); tp->t_args = arglist; tp->t_ellipsis = arglist->d_ellipsis; } return (tp); } /* * type_AddTail - add a type_t to the end of an existing type_t list. * * Note: * 1) The type_t *tp is added to the end of the type_t *dp list. */ static type_t * type_AddTail(type_t *dp, type_t *tp) { type_t *lastp = dp; type_t *p; while (p = lastp->t_next) lastp = p; lastp->t_next = tp; return (dp); } #if defined(DEBUG) /* type_PrintType - print a type_t list onto standard output. */ static void type_PrintType(type_t *tp, int lvl) { decl_spec_t tmp; char buf[BUFSIZ]; while (tp) { putNTabs(lvl); switch (tp->t_dt) { case DD_PTR: tmp.ds_stt = tp->t_stt; tmp.ds_id = NULL; printf("[%s] ptr to\n", declspec_ToString(buf, &tmp)); break; case DD_FUN: printf("fun [%d%c] %s\n", tp->t_nargs, (tp->t_ellipsis)? '+' : '=', (tp->t_args)? "with arguments" : "undefined arguments"); if (tp->t_args) { decl_PrintDecl(tp->t_args, lvl + 1); if (tp->t_ellipsis) { putNTabs(lvl + 1); printf("...\n"); } } break; case DD_ARY: printf("ary [%s] of\n", (tp->t_sizestr)? tp->t_sizestr : ""); break; } tp = tp->t_next; } } #endif /* DEBUG */ /* * type_Verify - verify a type_t list for semantic correctness. * * Note: * 1) C supports most combinations of type modifiers. * It does not support three combinations, they are: * * function returning array * array of functions * function returning function * * 2) The enum values associated with type modifiers (i.e. DD_*) * cannot be modified without changing the table included within the * function. * * 3) The function returns NULL to indicate that the type modifier * list is valid and non-NULL to indicate an error. * * 4) A type_t of NULL is permitted to indicate an empty type_t list. */ static const char * type_Verify(type_t *tp) { static const char *dttbl[4][4] = { /* NONE ARY FUN PTR */ /* NONE */ {NULL, NULL, NULL, NULL}, /* ARY */ {NULL, NULL, "array of functions", NULL}, /* FUN */ {NULL, "function returning array", "function returning function", NULL}, /* PTR */ {NULL, NULL, NULL, NULL}, }; if (tp) { type_t *nextp; do { const char *p; decl_type_t nt; nt = (nextp = tp->t_next)? nextp->t_dt : DD_NONE; if ((p = dttbl[tp->t_dt][nt]) != NULL) return (p); } while (tp = nextp); } return (NULL); } /* type_GetNext - return the next type_t in the list. */ type_t * type_GetNext(type_t *tp) { return (tp->t_next); } /* * The following group of functions return and or * test various aspects of type modifiers. * * 1) The three functions: type_IsPtrTo, type_IsFunction and * type_IsArray will accept an argument of NULL. * * 2) All other functions require one of the above three to be true. * Various asserts are in place to verify correct usage. */ int type_IsArray(type_t *tp) { return (tp && (tp->t_dt == DD_ARY)); } char * type_GetArraySize(type_t *tp) { assert(tp->t_dt == DD_ARY); return (tp->t_sizestr); } int type_IsPtrTo(type_t *tp) { return (tp && (tp->t_dt == DD_PTR)); } stt_t type_GetPtrToTypeQual(type_t *tp) { assert(tp->t_dt == DD_PTR); return (tp->t_stt); } int type_IsFunction(type_t *tp) { return (tp && (tp->t_dt == DD_FUN)); } int type_GetArgLength(type_t *tp) { assert(tp->t_dt == DD_FUN); return (tp->t_nargs); } int type_IsVarargs(type_t *tp) { while (tp && tp->t_dt == DD_PTR) tp = tp->t_next; assert(tp->t_dt == DD_FUN); return (tp->t_ellipsis); } decl_t * type_GetArg(type_t *tp) { assert(tp->t_dt == DD_FUN); return (tp->t_args); } /* * type_IsPtrFun - determine if the type_t results in a call-able function. * * Note: * 1) The argument can be NULL. * * 2) The test is true if the type_t list is number of DD_PTR followed * by a DD_FUN. */ int type_IsPtrFun(type_t *tp) { if (! (tp && (tp->t_dt == DD_PTR))) return (0); tp = tp->t_next; while (tp && (tp->t_dt == DD_PTR)) tp = tp->t_next; return (tp && (tp->t_dt == DD_FUN)); } /* D E C L A R A T O R */ /* * A decl_t encodes the name, * declaration specifiers and type modifiers of an object. */ /* decl_Construct - allocate a decl_t. */ static decl_t * decl_Construct(void) { decl_t *dp = malloc(sizeof (decl_t)); assert(dp != NULL); dp->d_name = NULL; dp->d_type = NULL; dp->d_next = NULL; dp->d_ds = declspec_Construct(); dp->d_ellipsis = 0; #if defined(MEM_DEBUG) ++decl_Construct_calls; #endif return (dp); } /* decl_Destroy - free a decl_t list. */ void decl_Destroy(decl_t *dp) { while (dp) { decl_t *nextp = dp->d_next; type_Destroy(dp->d_type); declspec_Destroy(dp->d_ds); free(dp->d_name); free(dp); dp = nextp; #if defined(MEM_DEBUG) --decl_Construct_calls; #endif } } /* * decl_GetArgLength - return the length of a decl_t list. * * Note: * 1) The argument may be NULL to indicate an empty list, len == 0. */ int decl_GetArgLength(decl_t *dp) { int len; for (len = 0; dp; dp = dp->d_next) ++len; return (len); } /* * The following group of functions get or test various aspects of a decl_t. */ decl_t * decl_GetNext(decl_t *dp) { return (dp->d_next); } stt_t decl_GetDeclSpec(decl_t *dp) { return (declspec_GetSTT(dp->d_ds)); } char * decl_GetDSName(decl_t *dp) { return (declspec_GetTag(dp->d_ds)); } type_t * decl_GetType(decl_t *dp) { return (dp->d_type); } int decl_IsVarargs(decl_t *dp) { return (dp->d_ellipsis); } int decl_IsFunction(decl_t *dp) { return (type_IsFunction(dp->d_type)); } char * decl_GetName(decl_t *dp) { return (dp->d_name); } /* * decl_AddArg - add a decl_t to the end of an decl_t list. */ static decl_t * decl_AddArg(decl_t *dp, decl_t *tp) { decl_t *lastp = dp; decl_t *p; while (p = lastp->d_next) lastp = p; lastp->d_next = tp; return (dp); } /* * decl_IsVoid - return true if the decl_t is a "pure" void declaration. */ static int decl_IsVoid(decl_t *dp) { return ((declspec_GetSTT(dp->d_ds) & TS_VOID) && (dp->d_type == NULL)); } /* * decl_IsVoidArray - return true if the decl_t includes "void []". */ static int decl_IsVoidArray(decl_t *dp) { int retval = 0; type_t *tp = dp->d_type; if (tp) { type_t *np; while (np = type_GetNext(tp)) tp = np; retval = type_IsArray(tp) && (declspec_GetSTT(dp->d_ds) & TS_VOID); } return (retval); } /* * decl_Verify - verify a decl_t. */ static const char * decl_Verify(decl_t *dp) { const char *ep = NULL; if (decl_IsVoid(dp)) ep = "type is void"; else if (decl_IsVoidArray(dp)) ep = "type is void []"; else ep = type_Verify(dp->d_type); return (ep); } /* * decl_VerifyArgs - verify a decl_t list. */ static const char * decl_VerifyArgs(decl_t *dp) { decl_t *tp = dp; const char *ep = NULL; if (dp) { int nv = 0; int nargs = decl_GetArgLength(dp); for (; dp; dp = dp->d_next) if (decl_IsVoid(dp)) { ++nv; if (decl_GetName(dp)) ep = "argument list includes " "void with identifier"; } else if (decl_IsVoidArray(dp)) ep = "argument list includes void []"; if (nv) { /* there was some void */ if (nargs > 1) ep = "argument list includes void"; if (tp->d_ellipsis) ep = "argument list includes void and \"...\""; } } return (ep); } /* decl_AddDS - add a decl_spec_t to a decl_t. */ static decl_t * decl_AddDS(decl_t *dp, decl_spec_t *dsp, const char **err) { declspec_AddDS(dp->d_ds, dsp, err); return (dp); } /* * decl_SetName - set the name associated with a decl_t. * * Note: * 1) Any previously known name is free'd. */ decl_t * decl_SetName(decl_t *dp, char *s) { free(dp->d_name); dp->d_name = strdup(s); assert(dp->d_name != NULL); return (dp); } /* * decl_AddTypeTail - add a type_t to the end of a decl_t type_t list. */ static decl_t * decl_AddTypeTail(decl_t *dp, type_t *tp) { if (dp->d_type) type_AddTail(dp->d_type, tp); else dp->d_type = tp; return (dp); } /* * decl_addptr - add a DD_PTR type_t to the end of a decl_t type_t list. */ static decl_t * decl_addptr(decl_t *dp, type_t *tp) { decl_AddTypeTail(dp, tp); return (dp); } /* * decl_addary - allocate and add a DD_ARY type_t to the end of * a decl_t type_t list. */ static decl_t * decl_addary(decl_t *dp, char *sizep) { type_t *tp = type_Construct(); type_SetAry(tp, sizep); decl_AddTypeTail(dp, tp); return (dp); } /* * decl_addfun - allocate and add a DD_FUN type_t to the end of a * decl_t type_t list. */ static decl_t * decl_addfun(decl_t *dp, decl_t *arglist) { const char *sp; type_t *tp = type_Construct(); if (sp = decl_VerifyArgs(arglist)) yyerror(sp); type_SetFun(tp, arglist); decl_AddTypeTail(dp, tp); return (dp); } /* * decl_addellipsis - set the ellipsis state in a decl_t. * * Note: * 1) This function is only used in the grammar in the * parameter list parsing. */ static decl_t * decl_addellipsis(decl_t *dp) { dp->d_ellipsis = 1; return (dp); } #if defined(DEBUG) static void decl_PrintDecl(decl_t *dp, int lvl) { char buf[BUFSIZ]; while (dp) { putNTabs(lvl); printf("name = %s, ds = %s\n", (dp->d_name)? dp->d_name : "", declspec_ToString(buf, dp->d_ds)); if (dp->d_type) type_PrintType(dp->d_type, lvl + 1); dp = dp->d_next; } } #endif /* DEBUG */ static char * char_getend(char *s) { while (*s != '\0') ++s; return (s); } char * decl_ToString(char *bufp, decl_dts_t out, decl_t *dp, const char *altname) { char tmp[BUFSIZ]; char tmp2[BUFSIZ]; const char *namep; char *bend = bufp; type_t *tp = dp->d_type; int ffun = 1; switch (out) { default: out = DTS_DECL; /* FALLTHRU */ case DTS_DECL: if (altname == NULL) { namep = dp->d_name; } else { namep = altname; } break; case DTS_CAST: namep = "(*)"; break; case DTS_RET: if (altname == NULL) { namep = "_return"; } else { namep = altname; } break; } *bufp = '\0'; strcpy(tmp, (namep) ? namep : ""); while (tp) { switch (tp->t_dt) { case DD_PTR: if (tp->t_next && ((tp->t_next->t_dt == DD_ARY) || (tp->t_next->t_dt == DD_FUN))) { if (out == DTS_RET) { sprintf(bufp, "(*%s)", namep); } else { sprintf(bufp, "(*%s)", tmp); } } else if (tp->t_stt == TQ_CONST) { sprintf(bufp, "*const %s", tmp); } else if (tp->t_stt == TQ_VOLATILE) { sprintf(bufp, "*volatile %s", tmp); /* * It currently acknowledges and ignores restrict * or _RESTRICT_KYWD in code generation because * of the uncertain behavior of "restrict". */ } else if (tp->t_stt == TQ_RESTRICT) { sprintf(bufp, "*%s", tmp); } else if (tp->t_stt == TQ_RESTRICT_KYWD) { sprintf(bufp, "*%s", tmp); } else { sprintf(bufp, "*%s", tmp); } break; case DD_ARY: sprintf(bufp, "%s[%s]", tmp, (tp->t_sizestr)? tp->t_sizestr : ""); break; case DD_FUN: if (out == DTS_RET && ffun == 1) { strcpy(bufp, namep); ffun = 0; } else if (tp->t_args == NULL) { sprintf(bufp, "%s()", tmp); } else { char buf2[BUFSIZ]; decl_t *argp = tp->t_args; sprintf(bufp, "%s(", tmp); bend = char_getend(bufp); for (argp = tp->t_args; argp; /* noinc */) { decl_ToString(buf2, DTS_DECL, argp, NULL); sprintf(bend, " %s", buf2); bend = char_getend(bend); if (argp = argp->d_next) { sprintf(bend, ","); bend = char_getend(bend); } } if (tp->t_ellipsis) { sprintf(bend, ", ..."); bend = char_getend(bend); } sprintf(bend, ")"); } break; } tp = tp->t_next; strcpy(tmp, bufp); } if (out == DTS_CAST) { sprintf(bufp, "(%s %s)", declspec_ToString(tmp2, dp->d_ds), tmp); } else { sprintf(bufp, "%s %s", declspec_ToString(tmp2, dp->d_ds), tmp); } return (bufp); } decl_t * decl_AddArgNames(decl_t *dp) { int argno = 0; decl_t *p = dp; if (decl_IsFunction(dp)) { int argno = 0; decl_t *p = dp->d_type->t_args; while (p) { char *s = decl_GetName(p); if ((s == NULL) && !decl_IsVoid(p)) { char buf[BUFSIZ]; sprintf(buf, "arg%d", argno); s = strdup(buf); decl_SetName(p, s); } p = p->d_next; ++argno; } } return (dp); } const char * decl_Parse(char *str, decl_t **dpp) { errstr = NULL; /* setup the (static) globals */ input = str; atIDENT = 0; protop = NULL; yyparse(); /* parse the prototype */ if (errstr == NULL) { /* success */ *dpp = protop; decl_AddArgNames(protop); } else { /* failure */ *dpp = NULL; decl_Destroy(protop); } return (errstr); } static void yyerror(const char *err) { errstr = err; } #if defined(DEBUG) /* main */ static int yydebug = 1; int main(int argc, char *argv[]) { int i; yydebug = 1; for (i = 1; i < argc; ++i) { const char *es; char buf[BUFSIZ]; decl_t *pp; if (es = decl_Parse(argv[i], &pp)) printf("parse failure: %s\n", es); else { #if GR_DEBUG decl_PrintDecl(pp, 0); decl_AddArgNames(pp); #endif printf("---\n%s;\n", decl_ToString(buf, DTS_DECL, pp, NULL)); printf("%s\n", decl_ToString(buf, DTS_CAST, pp, NULL)); printf("%s;\n", decl_ToString(buf, DTS_RET, pp, "%s")); #ifdef TRACE printf("\n\nTrace Info\n"); decl_PrintTraceInfo(pp); #endif } decl_Destroy(pp); #if defined(MEM_DEBUG) printf("declspec : %d\n", declspec_Construct_calls); printf("type : %d\n", type_Construct_calls); printf("decl : %d\n", decl_Construct_calls); #endif } return (0); } #ifdef TRACE void decl_PrintTraceInfo(decl_t *dp) { char buf[BUFSIZ]; char f_type[BUFSIZ]; char f_print[BUFSIZ]; char a_name[BUFSIZ]; char a_type[BUFSIZ]; char a_print[BUFSIZ]; decl_t *funargs; type_t *tp; int isptrfun; if (dp == NULL) return; fprintf(stderr, "interface = %s\n", (dp->d_name) ? dp->d_name : ""); isptrfun = type_IsPtrFun(dp->d_type); if (type_IsFunction(dp->d_type) || isptrfun) decl_GetTraceInfo(dp, f_type, f_print, &funargs); else return; fprintf(stderr, "return type = %s\n", f_type); fprintf(stderr, "print function = %s\n", f_print); if (isptrfun) fprintf(stderr, "function is function pointer\n"); if (type_IsVarargs(dp->d_type)) fprintf(stderr, "function is varargs\n"); while (funargs) { snprintf(a_type, BUFSIZ, "%s ", declspec_ToString(buf, funargs->d_ds)); snprintf(a_print, BUFSIZ, "%s", de_const(declspec_ToString(buf, funargs->d_ds))); tp = funargs->d_type; while (tp) { if (tp->t_dt == DD_PTR || tp->t_dt == DD_ARY) { strcat(a_type, "*"); strcat(a_print, "_P"); } tp = tp->t_next; } if (funargs->d_name) { snprintf(a_name, BUFSIZ, "%s", funargs->d_name ? funargs->d_name : ""); fprintf(stderr, "arg name = %s\n", a_name); fprintf(stderr, "arg type = %s\n", a_type); fprintf(stderr, "print function = %s\n", a_print); } else { strcpy(a_name, ""); strcpy(a_print, ""); fprintf(stderr, "arg type = %s\n", a_type); } funargs = funargs->d_next; } } #endif /* TRACE */ #endif /* DEBUG */ static char * de_const(char *str) { return (str); } void decl_GetTraceInfo(decl_t *dp, char *f_type, char *f_print, decl_t **funargs) { char buf[BUFSIZ]; type_t *tp; if (dp == NULL) return; snprintf(f_type, BUFSIZ, "%s ", declspec_ToString(buf, dp->d_ds)); snprintf(f_print, BUFSIZ, "%s", de_const(declspec_ToString(buf, dp->d_ds))); tp = dp->d_type; while (tp) { if (tp->t_dt == DD_PTR) { strcat(f_type, "*"); strcat(f_print, "*"); } tp = tp->t_next; } strcat(f_type, "%s"); tp = decl_GetType(dp); if (type_IsPtrFun(tp)) { while (tp->t_dt != DD_FUN) tp = tp->t_next; *funargs = tp->t_args; } else { *funargs = dp->d_type->t_args; } } char * decl_ToFormal(decl_t *dp) { char tmp[BUFSIZ]; static char bufp[BUFSIZ]; char *bend; type_t *tp = dp->d_type; tmp[0] = 0; bufp[0] = 0; bend = bufp; while (tp) { switch (tp->t_dt) { case DD_ARY: sprintf(bufp, "%s[%s]", tmp, (tp->t_sizestr)? tp->t_sizestr : ""); break; case DD_FUN: if (tp->t_args != NULL) { char buf2[BUFSIZ]; decl_t *argp = tp->t_args; bend = char_getend(bufp); for (argp = tp->t_args; argp; /* noinc */) { decl_ToString(buf2, DTS_DECL, argp, NULL); sprintf(bend, " %s", buf2); bend = char_getend(bend); if (argp = argp->d_next) { sprintf(bend, ","); bend = char_getend(bend); } } if (tp->t_ellipsis) { sprintf(bend, ", ..."); bend = char_getend(bend); } sprintf(bend, ""); } break; } tp = tp->t_next; strcpy(tmp, bufp); } sprintf(bufp, "%s", tmp); return (bufp); }