/* * PARSE2.C - Misc parser support, including declarator handling * * (c)Copyright 1993-2014, Matthew Dillon, All Rights Reserved. See the * COPYRIGHT file at the base of the distribution. */ #include "defs.h" static int FinishDecl(Parse *p, int t, Declaration *d, Type *type); /* * Parse scope qualifiers. * * XXX handle scope overrides */ int ParseScopeQual(Parse *p, int t, Scope *scope) { scope->s_Flags = 0; scope->s_AlignOverride = 0; while (t & TOKF_SCOPE_QUAL) { scope->s_Flags |= 1 << (t & TOKF_SCOPE_MASK); if (t == TOK_ALIGN) { t = LexToken(&p->p_Token); t = LexSkipToken(&p->p_Token, TOK_OPAREN); t = SimpleIntToken(&p->p_Token, &scope->s_AlignOverride); t = LexSkipToken(&p->p_Token, TOK_INTEGER); t = LexSkipToken(&p->p_Token, TOK_CPAREN); } else { t = LexToken(&p->p_Token); } } /* * Persistent storage is represented as an lvalue element * Heap storage is represented as an lvalue element. * * lvalue elements are represented as an LValueStor structure * which points to the object. Even though this is physically a * pointer, actions occur on the underlying object as if it were * an lvalue. */ if (scope->s_Flags & (SCOPE_PERSIST | SCOPE_HEAP)) scope->s_Flags |= SCOPE_LVALUE; return(t); } /* * XXX handle illegal storage qualifier combinations & overrides */ int ParseStorageQual(Parse *p, int t, int *pmask) { int mask = 0; while (t & TOKF_STOR_QUAL) { mask |= 1 << (t & TOKF_STOR_MASK); t = LexToken(&p->p_Token); } *pmask = mask; return(t); } /* * declaration: type decl(idReq) [= exp] * * idRequired may be 0, 1, or 2. * * 0: No identifier is required * 1: An identifier is required * 2: An identifier is required, a dotted identifier sequence is * optional, and global scope is implied if only a single * identifier is used. * * NOTE: We only handle the procedural reference portion of a procedure * definition here. We do not handle the procedure body. See * ParseDeclarationStmt(). We also handle aliases here. * * NOTE: TOK_ALIAS can be passed in and will handle alias declarations, * including converting the declaration to DOP_ALIAS. */ int ParseDeclaration(Parse *p, int t, Declaration **pd, int idRequired, int dop, Scope *scope) { Declaration *d; int sqflags = 0; int rqflags = 0; int isAlias = 0; int isCast = 0; int isOper = 0; int isMeth = 0; string_t id = NULL; string_t opid = NULL; Type *type; Scope tscope = *scope; /* * Handle alias declarations. */ if (t == TOK_ALIAS) { isAlias = 1; t = LexToken(&p->p_Token); } /* * Handle procedure-related special cases */ switch(t) { case TOK_CAST: t = LexToken(&p->p_Token); tscope.s_Flags |= SCOPE_CAST; isCast = 1; break; case TOK_OPERATOR: t = LexToken(&p->p_Token); if (t != TOK_DSTRING) { t = LexError(&p->p_Token, TOK_ERR_EXPECTED_QUOTED_OPERATOR); } else { /* * Add the operator to the operator hash. Don't * hash the quotes, just the contents of the quotes. */ opid = StrTableAlloc(p->p_Token.t_Ptr + 1, p->p_Token.t_Len - 2, 0); t = LexToken(&p->p_Token); } isOper = 1; break; case TOK_METHOD: t = LexToken(&p->p_Token); isMeth = 1; if (tscope.s_Flags & SCOPE_GLOBAL) rqflags |= SF_GMETHOD; else rqflags |= SF_METHOD; break; } /* * Allow the LVALUE scope to be specified after the operator * (to make operator declarations using lvalue look nicer). * * 'lvalue' is also a storage qualifier, and requires outer scoping. * e.g. if you have something like 'lvalue const int *x', lvalue * applies to the declaration itself, the pointer, whereas const * applies to what is being pointed to. */ if (t == TOK_LVALUE) { tscope.s_Flags |= SCOPE_LVALUE; t = LexToken(&p->p_Token); } if (tscope.s_Flags & SCOPE_LVALUE) rqflags |= SF_LVALUE; /* * Parse storage qualifiers and the base type */ t = ParseStorageQual(p, t, &sqflags); t = ParseType(p, t, &type, sqflags); /* * Parse the declarators and identifier(s). Deal with implied global * scope, and adjust 'dop' properly. */ if (type) { SemGroup *sg = p->p_CurSemGroup; t = ParseDeclType(p, t, &type, &sg, tscope.s_Flags & SCOPE_CLANG, rqflags, &id, idRequired); if (idRequired == 2 && sg == p->p_CurSemGroup) tscope.s_Flags |= SCOPE_GLOBAL; if ((tscope.s_Flags & SCOPE_GLOBAL) && (dop & DOPF_STORAGE)) dop = DOP_GLOBAL_STORAGE; if (isAlias) dop = DOP_ALIAS; d = AllocDeclaration(sg, dop, &tscope); t = FinishDecl(p, t, d, type); } else { d = NULL; } /* * Make sure procedural keywords are only used with procedures, * and hash the operator id (if any). */ if (d) { if (d->d_Op != DOP_PROC && (isCast || isOper || isMeth)) { t = LexError(&p->p_Token, TOK_ERR_OPERATOR_NOT_PROCEDURE); } else if (opid) { HashOper(d, opid); } } #if 0 /* * If this is a method procedure we have to add an argument called * 'this' that represents an lvalue of the class the procedure resides * in. XXX not needed any more. */ if (isMeth) { } #endif /* * Handle any assigned expression */ if (t == TOK_ASS) { switch(d->d_Op) { case DOP_ALIAS: t = LexToken(&p->p_Token); t = ParseExp(p, t, &d->d_AliasDecl.ed_AssExp); break; case DOP_TYPEDEF: t = LexToken(&p->p_Token); t = ParseExp(p, t, &d->d_TypedefDecl.ed_AssExp); break; case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: t = LexToken(&p->p_Token); t = ParseExp(p, t, &d->d_StorDecl.ed_AssExp); break; } } else if (isAlias) { /* * Assignment is required for aliases */ t = LexError(&p->p_Token, TOK_ERR_SYNTAX); } if (t & TOKF_ERROR) { if (d) { FreeDeclaration(p, d); d = NULL; } if (id) RelStrTable(&id); } else { if (id) { /* * NOTE: HashDecl() eats our reference on id. * * Allow overloading for operators. * * We currently do not support overloading of internal * identifiers or overload-qualified procedures (it * makes superclass refinement a nightmare). */ if (HashDecl(d, id) > 0 && /* (d->d_ScopeFlags & SCOPE_INTERNAL) == 0 && */ /* (d->d_ScopeFlags & SCOPE_OVERLOAD) == 0 && */ !(d->d_Op == DOP_PROC && d->d_ProcDecl.ed_OperId != NULL)) { t = LexError(&p->p_Token, TOK_ERR_DUPLICATE_ID); *(int *)0 = 1; } #if 0 if ((d->d_ScopeFlags & SCOPE_OVERLOAD) && d->d_Op != DOP_PROC) { t = LexError(&p->p_Token, TOK_ERR_DUPLICATE_ID); } #endif } } if (pd) *pd = d; return(t); } /* * ( declaration [, declaration]* ) */ int ParseDeclarationList(Parse *p, int t, int scopeFlags, int idRequired, int dop) { t = LexSkipToken(&p->p_Token, TOK_OPAREN); if (t != TOK_CPAREN) { for (;;) { Scope scope; if (t == TOK_DOT) { if (p->p_CurSemGroup->sg_Type == SG_PROCARGS) { t = LexSkipToken(&p->p_Token, TOK_DOT); t = LexSkipToken(&p->p_Token, TOK_DOT); t = LexSkipToken(&p->p_Token, TOK_DOT); p->p_CurSemGroup->sg_Flags |= SGF_VARARGS; } else { t = LexError(&p->p_Token, TOK_ERR_SYNTAX); } break; } t = ParseScopeQual(p, t, &scope); scope.s_Flags |= scopeFlags; t = ParseDeclaration(p, t, NULL, idRequired, dop, &scope); if (t != TOK_COMMA) break; t = LexToken(&p->p_Token); } } t = LexSkipToken(&p->p_Token, TOK_CPAREN); return(t); } /* * type: * id [ . id ]* * ( declaration [, declaration]* ) */ int ParseType(Parse *p, int t, Type **ptype, int sqflags) { *ptype = NULL; if (t == TOK_STORAGE) { runesize_t bytes = 0; t = LexToken(&p->p_Token); t = LexSkipToken(&p->p_Token, TOK_OPAREN); t = SimpleRunesizeToken(&p->p_Token, &bytes); t = LexSkipToken(&p->p_Token, TOK_INTEGER); t = LexSkipToken(&p->p_Token, TOK_CPAREN); *ptype = AllocStorageType(bytes); if (sqflags) { *ptype = TypeToQualType(*ptype, NULL, (*ptype)->ty_SQFlags|sqflags, NULL); } return(t); } if (t == TOK_OPAREN) { /* * parse a compound type. */ p->p_CurSemGroup = AllocSemGroup(SG_COMPOUND, p, p->p_CurSemGroup, NULL); t = ParseDeclarationList(p, t, 0, 0, DOP_GROUP_STORAGE); if ((t & TOKF_ERROR) == 0) { *ptype = AllocCompoundType(p->p_CurSemGroup); if (sqflags) { *ptype = TypeToQualType(*ptype, NULL, (*ptype)->ty_SQFlags | sqflags, NULL); } } else { t = LexError(&p->p_Token, TOK_ERR_EXPECTED_CPAREN); /* mark semgroup for free after pop */ } p->p_CurSemGroup = p->p_CurSemGroup->sg_Parent; } else if (t & TOKF_ID) { /* * parse a normal type, which is a semantic identifier path * like "a.b.c.d". */ string_t *ary; int count; int lt; t = ParseDotIdAry(p, t, &ary, &count, <); if (lt != TOK_CLASSID) t = LexError(&p->p_Token, TOK_ERR_EXPECTED_CLASSID); if ((t & TOKF_ERROR) == 0) { *ptype = AllocUnresolvedType( p->p_Import->st_ImportStmt.es_SemGroup, p->p_CurSemGroup, ary, 1); } /* XXX cleanup memory */ } else { t = LexError(&p->p_Token, TOK_ERR_EXPECTED_TYPE); } if ((t & TOKF_ERROR) == 0) { if (sqflags) { *ptype = TypeToQualType(*ptype, NULL, (*ptype)->ty_SQFlags | sqflags, NULL); } } return(t); } /* * ParseDotIdAry() - parse a sequence of dot-separated identifiers. */ int ParseDotIdAry(Parse *p, int t, string_t **pary, int *pcount, int *ltp) { string_t *ary; token_t save; int count; /* * First token must be an identifier */ if ((t & TOKF_ID) == 0) t = LexError(&p->p_Token, TOK_ERR_EXPECTED_ID); /* * First count the number of identifiers. */ save = p->p_Token; count = 0; *ltp = 0; while ((t & TOKF_ERROR) == 0) { ++count; /* count ID */ *ltp = t; t = LexToken(&p->p_Token); /* skip id */ if (t != TOK_DOT) break; t = LexToken(&p->p_Token); /* skip dot */ if ((t & TOKF_ID) == 0) t = LexError(&p->p_Token, TOK_ERR_EXPECTED_ID); } /* * If no error occured, allocate the array and load it with the * identifiers. */ if ((t & TOKF_ERROR) == 0) { int i; p->p_Token = save; t = save.t_Type; ary = zalloc(sizeof(string_t) * (count + 1)); for (i = 0; i < count; ++i) { ary[i] = StrTableToken(&p->p_Token); t = LexToken(&p->p_Token); /* skip id */ if (i + 1 < count) t = LexToken(&p->p_Token); /* skip dot */ } *pary = ary; *pcount = count; } else { *pary = NULL; *pcount = 0; } return(t); } string_t * DupDotIdAry(string_t *ary) { int i; string_t *nary; for (i = 0; ary[i]; ++i) ; nary = zalloc(sizeof(string_t) * (i + 1)); for (i = 0; ary[i]; ++i) nary[i] = StrTableDup(ary[i]); return(nary); } void FreeDotIdAry(string_t *ary) { int i; for (i = 0; ary[i]; ++i) RelStrTable(&ary[i]); zfree(ary, sizeof(string_t) * (i + 1)); } /* * ParseDeclType() - Parse declarators for declaration given base type * * Parse the declarators for the specified base type, returning an * appropriate qualified type. If an identifier is part of the * declarator sequence the identifier will be returned. If idRequired * is 1, an identifier is required. If idRequired is 2, multiple * dot-separated identifiers are allowed (and the last is returned), * and psg will be set to the SemGroup representing the class they * reference. For example, 'int FILE.setMode(int mode) { .. }' * will set *psg to FILE's SemGroup. This eventually goes into * d_MyGroup, while d_OrigSemGroup retains the original semgroup. * * Note that multi-identifiers have library scope only and must already * exist (not be forward referenced). * * declaration: type decl * * decl: [ declarator ]* * * declarator: * id[.id]* identifier(s) (if not already specified) * ( declaration_list) procedure-returning * [ exp ] array of * * pointer to * [ storage_qual ]* qualified with * * NOTE! Types are built up from left-to-right prior to the identifier or * procedure args, then right-to-left. For example: * * const char * const a[2][10] - an array 2 of an array 10 of * a constant pointer to a constant * character. * * int func * (int, int) - a pointer to a procedure taking * two arguments and returning an int. * * NOTE! Certain storage qualifiers in an underlying type will migrate to * an array type. * * NOTE! scopeFlags propogates SCOPE_CLANG, which effects pointers * (XXX and should effect alignment too??) */ int ParseDeclType(Parse *p, int t, Type **ptype, SemGroup **psg, int scopeFlags, int rqflags, string_t *id, int idRequired) { for (;;) { if (t == TOK_OPAREN) { /* * parse procedure argument-type, which is a compound * type. Note that we leave our semantic linkages * intact to simplify the type resolver (see * resolve.c), but set the DOP to something we can * recognize later. The nesting level will be fixed * up later. */ p->p_CurSemGroup = AllocSemGroup(SG_PROCARGS, p, p->p_CurSemGroup, NULL); t = ParseDeclarationList(p, t, scopeFlags, 0, DOP_ARGS_STORAGE); if (*id) t = ParseDeclType(p, t, ptype, NULL, scopeFlags, 0, id, idRequired > 1 ? 1 : idRequired); *ptype = TypeToProcType(*ptype, AllocArgsType(p->p_CurSemGroup)); p->p_CurSemGroup = p->p_CurSemGroup->sg_Parent; /* * anything after proc args must be parsed as if * we had specified an identifier. */ if (*id == NULL) *id = StrTableAlloc("_dummy_", 7, 0); } else if (t == TOK_AT) { /* * A reference type (bound via a superclass) */ t = LexToken(&p->p_Token); if (*id) { t = ParseDeclType(p, t, ptype, psg, scopeFlags, 0, id, idRequired); } *ptype = TypeToRefType(*ptype); } else if (t == TOK_OPER && p->p_Token.t_Ptr[0] == '*') { /* * We have to deal with pointer-to declarators. * Regenerate the token using only the first character * if it is a '*'. */ t = LexRedactToken(&p->p_Token, 1); t = LexToken(&p->p_Token); if (*id) { t = ParseDeclType(p, t, ptype, psg, scopeFlags, 0, id, idRequired); } if (scopeFlags & SCOPE_CLANG) *ptype = TypeToCPtrType(*ptype); else *ptype = TypeToPtrType(*ptype); } else if (t == TOK_OBRACKET) { /* * Array */ Exp *exp = NULL; t = LexToken(&p->p_Token); t = ParseExp(p, t, &exp); t = LexSkipToken(&p->p_Token, TOK_CBRACKET); if (*id) { t = ParseDeclType(p, t, ptype, psg, scopeFlags, 0, id, idRequired); } *ptype = TypeToAryType(*ptype, exp, *psg); } else if (t & TOKF_SCOPE_QUAL) { t = LexError(&p->p_Token, TOK_ERR_SYNTAX); } else if (t & TOKF_STOR_QUAL) { int sqflags; t = ParseStorageQual(p, t, &sqflags); if (*id) { t = ParseDeclType(p, t, ptype, psg, scopeFlags, 0, id, idRequired); } *ptype = TypeToQualType(*ptype, NULL, (*ptype)->ty_SQFlags | sqflags, NULL); } else if (t & TOKF_ID) { if (*id) /* terminator */ break; *id = StrTableToken(&p->p_Token); t = LexToken(&p->p_Token); if (idRequired < 2 && t == TOK_DOT) { t = LexError(&p->p_Token, TOK_ERR_NO_DOTTED_ID_HERE); } while (t == TOK_DOT) { /* * Lookup *id based on the current psg. This * is about the only FindDeclPath() call that * occurs during parse time. All the rest * occur at resolve time. There is no * skip-back SemGroup at parse time (i.e. * first arg is NULL). * * At the moment we only allow private scope. * This is because we need pre-resolution * structural linkages to stay within their * import paradigm, allowing us to save and * restore/relocate the associated memory pool * as an intermediate pre-parsed file format. * It also allows us to resolve things * incrementally and save/restore after those * steps as well. * * At some future date I intend to allow * library scope, (SCOPE_PRIVATE|SCOPE_LIBRARY), * which will necessitate saving and restoring * dependant files as a single entity. * XXX */ Declaration *d; int vis = SCOPE_LIBRARY|SCOPE_PRIVATE; int eno = TOK_ERR_PARSE_CLASS_NOT_FOUND; string_t ary[] = { *id, NULL }; d = FindDeclPath(NULL, NULL, *psg, NULL, ary, 0, &vis, -1, &eno); if (d) { switch(d->d_Op) { case DOP_CLASS: *psg = d->d_ClassDecl.ed_SemGroup; break; case DOP_IMPORT: *psg = d->d_ImportDecl.ed_SemGroup; break; default: t = LexError(&p->p_Token, TOK_ERR_PARSE_ID_NOT_CLASS); break; } } else { t = LexError(&p->p_Token, eno); } if (t & TOKF_ERROR) break; /* * Retrieve the next identifier */ t = LexToken(&p->p_Token); if ((t & TOKF_ID) == 0) { t = LexError(&p->p_Token, TOK_ERR_EXPECTED_ID); break; } ReplaceStrTable(id, StrTableToken(&p->p_Token)); t = LexToken(&p->p_Token); } } else if ( t == TOK_CPAREN || t == TOK_SEMI || t == TOK_COMMA || t == TOK_ASS || t == TOK_OBRACE ) { break; /* terminator */ } else { t = LexError(&p->p_Token, TOK_ERR_EXPECTED_DECLARATOR); } if (t & TOKF_ERROR) break; } /* * A Method procedure silently passes the lvalue object as the first * argument to the procedure. A global method procedure silently * passes the type of the object as the first argument to the * procedure (as a typedef). The argument is called this. If the * procedure declares a 'this' argument for the first argument, we * assume that the programmer has constructed this special argument * manually (which the programmer must do if he wants an lvalue * pointer or reference to the class). */ if (rqflags & (SF_METHOD|SF_GMETHOD)) { Type *type; Type *ctype; Stmt *st; SemGroup *sg; Declaration *nd; type = *ptype; dassert_parse(p, 0, type->ty_Op == TY_PROC); nd = RUNE_FIRST(&type->ty_ProcType.et_ArgsType-> ty_ArgsType.et_SemGroup->sg_DeclList); if (nd && nd->d_Id != String_This) nd = NULL; /* * XXX should really use TypeToProcType() or write a * ProcTypeToMethodProcType() call. */ sg = *psg; st = sg->sg_Stmt; if (st->st_Op != ST_Class) { fprintf(stderr, "Method calls must be placed in a class\n"); dassert_parse(p, 0, st->st_Op == ST_Class); } ctype = AllocClassType(&sg->sg_ClassList, st->st_ClassStmt.es_Super, sg, SCOPE_ALL_VISIBLE); if (rqflags & SF_METHOD) { type = TypeToQualType(type, NULL, type->ty_SQFlags | SF_METHOD, NULL); } else { dassert(rqflags & SF_GMETHOD); type = TypeToQualType(type, NULL, type->ty_SQFlags | SF_GMETHOD, NULL); } if (nd == NULL) { Scope tscope = INIT_SCOPE(SCOPE_ALL_VISIBLE); sg = type->ty_ProcType.et_ArgsType-> ty_ArgsType.et_SemGroup; nd = RUNE_FIRST(&sg->sg_DeclList); if (rqflags & SF_METHOD) { nd = AllocDeclaration(sg, DOP_ARGS_STORAGE, &tscope); nd->d_StorDecl.ed_Type = TypeToQualType(ctype, NULL, ctype->ty_SQFlags | SF_LVALUE, NULL); nd->d_ScopeFlags |= SCOPE_LVALUE; } else { nd = AllocDeclaration(sg, DOP_TYPEDEF, &tscope); nd->d_TypedefDecl.ed_Type = ctype; } HashDecl(nd, StrTableDup(String_This)); /* * We really want the declaration at the head, not * the tail. */ RUNE_REMOVE(&sg->sg_DeclList, nd, d_Node); RUNE_INSERT_HEAD(&sg->sg_DeclList, nd, d_Node); } *ptype = type; rqflags &= ~(SF_METHOD | SF_GMETHOD); } if (rqflags) { Type *type = *ptype; if (type->ty_Op == TY_PROC) { type = type->ty_ProcType.et_RetType; (*ptype)->ty_ProcType.et_RetType = TypeToQualType(type, NULL, type->ty_SQFlags | rqflags, NULL); } else { *ptype = TypeToQualType(type, NULL, type->ty_SQFlags | rqflags, NULL); } } return(t); } static int FinishDecl(Parse *p, int t, Declaration *d, Type *type) { /* * Install the type and adjust the declaration accordingly. */ if (d->d_Op == DOP_ALIAS) { d->d_StorDecl.ed_Type = type; } else if (d->d_Op == DOP_TYPEDEF) { if (type->ty_Op == TY_PROC) t = LexError(&p->p_Token, TOK_ERR_ILLEGAL_TYPEDEF); else d->d_TypedefDecl.ed_Type = type; } else if (type->ty_Op == TY_PROC) { d->d_Op = DOP_PROC; d->d_ProcDecl.ed_Type = type; } else { dassert_decl(d, d->d_Op & DOPF_STORAGE); d->d_StorDecl.ed_Type = type; } /* * Install d_ImportSemGroup. */ d->d_ImportSemGroup = p->p_Import->st_ImportStmt.es_SemGroup; return(t); }