/* pfBindVars - bind variables into parse tree. Fill in types of * nodes containing variables. */ /* Copyright 2005 Jim Kent. All rights reserved. */ #include "common.h" #include "hash.h" #include "pfToken.h" #include "pfType.h" #include "pfParse.h" #include "pfCompile.h" #include "pfBindVars.h" static void evalFunctionType(struct pfParse *pp, struct pfBaseType *base, enum pfTyty tyty) /* Fill in type info on to/para/flow node. */ { struct pfParse *name = pp->children; struct pfParse *input = name->next; struct pfParse *output = input->next; struct pfType *ty = pfTypeNew(base); ty->tyty = tyty; pp->ty = ty; ty->children = input->ty; if (output->ty == NULL) errAt(output->tok, "Expecting type before %s", pp->name); ty->children->next = output->ty; } static void evalFunctionPtType(struct pfCompile *pfc, struct pfParse *pp, struct pfBaseType *base) /* Fill in pp->ty for a function pointer node */ { struct pfParse *input = pp->children; struct pfParse *output = input->next; struct pfType *ty = pfTypeNew(base); ty->tyty = tytyFunctionPointer; pp->ty = ty; ty->children = input->ty; ty->children->next = output->ty; } static void evalDeclarationTypes(struct pfCompile *pfc, struct pfParse *pp, int level) /* Go through and fill in pp->ty field on type expressions related * to function and variable declarations. */ { struct pfParse *p; for (p = pp->children; p != NULL; p = p->next) evalDeclarationTypes(pfc, p, level+1); switch (pp->type) { case pptVarDec: internalErr(); break; case pptVarInit: case pptFormalParameter: { struct pfParse *type = pp->children; struct pfParse *name = type->next; struct pfParse *init = name->next; pp->ty = CloneVar(type->ty); pp->ty->init = init; break; } case pptToDec: evalFunctionType(pp, pfc->toType, tytyFunction); break; case pptFlowDec: evalFunctionType(pp, pfc->flowType, tytyFunction); break; case pptOperatorDec: evalFunctionType(pp, pfc->operatorType, tytyOperator); break; case pptTypeToPt: evalFunctionPtType(pfc, pp, pfc->toPtType); break; case pptTypeFlowPt: evalFunctionPtType(pfc, pp, pfc->flowPtType); break; case pptKeyVal: { /* Here we allow either strings or naked names on the * left of a pptKeyVal. */ struct pfParse *key = pp->children; if (key->type != pptNameUse && key->type != pptConstUse) errAt(pp->tok, "The key in a key : val expression must be string or name."); key->type = pptKeyName; key->tok->type = pftString; key->name = key->tok->val.s; break; } case pptForeach: case pptParaDo: case pptParaAdd: case pptParaMultiply: case pptParaAnd: case pptParaOr: case pptParaMin: case pptParaMax: case pptParaArgMin: case pptParaArgMax: case pptParaGet: case pptParaFilter: { /* Allow users to skip the type declaration of the * element in a (el in collection) phrase. * I wonder if we could move this to pfParse * actually. WOrks here though. */ struct pfParse *collection = pp->children; struct pfParse *element = collection->next; if (element->type == pptNameUse) { } else if (element->type == pptKeyVal) { struct pfParse *keyVal = element; struct pfParse *key = keyVal->children; element = key->next; if (element->type != pptNameUse || key->type != pptKeyName) errAt(keyVal->tok, "Expecting simple name on either side of ':'"); key->var = pfScopeAddVar(key->scope, key->name, pfTypeNew(pfc->nilType), key); } else { errAt(element->tok, "Can't handle this construct before 'in.' \n" "Expecting variable name with no type, or key:val."); } element->type = pptUntypedElInCollection; element->var = pfScopeAddVar(element->scope, element->name, pfTypeNew(pfc->nilType), element); break; } case pptOf: { /* The symbols 'array of dir of string' get converted * into a 'pptOf' parse array with children array,dir,string. * We convert this into a type heirachy with array at top, * then dir, then string. To do this we mix child and * next pointers in a somewhat unholy fashion. */ struct pfParse *type = pp->children; pp->ty = type->ty; while (type != NULL) { struct pfParse *childType = type->next; if (childType != NULL) type->ty->children = childType->ty; type = childType; } break; } case pptModuleDotType: { struct pfParse *modPp = pp->children; struct pfParse *typePp = modPp->next; struct pfVar *var = pfScopeFindVar(pp->scope, modPp->name); char *moduleName; struct pfModule *module; struct pfBaseType *base; struct pfType *ty; if (var == NULL) internalErrAt(pp->tok); moduleName = var->parse->children->name; module = hashFindVal(pfc->moduleHash, moduleName); if (module == NULL) internalErrAt(modPp->tok); base = pfScopeFindType(module->scope, typePp->name); if (base == NULL) errAt(typePp->tok, "Class %s not defined in module %s", typePp->name, moduleName); ty = pfTypeNew(base); pp->ty = typePp->ty = ty; modPp->type = pptModuleUse; typePp->type = pptTypeName; typePp->scope = module->scope; break; } case pptTypeName: { struct pfBaseType *base = pfScopeFindType(pp->scope, pp->name); if (base != NULL) { struct pfType *ty = pfTypeNew(base); ty->access = pp->access; ty->isConst = pp->isConst; pp->ty = ty; } else errAt(pp->tok, "Undefined class %s", pp->name); break; } case pptTypeTuple: { pfTypeOnTuple(pfc, pp); break; } } } static void checkNoOutput(struct pfParse *pp) /* The pp parse tree for the method looks like so: * pptFlowDec (or pptToDec) * pptSymName * pptTuple (input parameters) * ... * pptTuple (output parameters) * ... * This function makes sure that the output parameters are empty. */ { struct pfParse *name = pp->children; struct pfParse *input = name->next; struct pfParse *output = input->next; if (output->children != NULL) errAt(output->tok, "create method can't have any output."); } static void addDeclaredVarsToScopes(struct pfCompile *pfc, struct pfParse *pp) /* Go through and put declared variables into symbol table * for scope. */ { switch (pp->type) { case pptVarDec: internalErr(); break; case pptVarInit: { struct pfParse *type = pp->children; struct pfParse *name = type->next; pp->ty->access = pp->access; pp->ty->isConst = pp->isConst; pp->var = pfScopeAddVar(pp->scope, name->name, pp->ty, pp); break; } case pptToDec: case pptFlowDec: case pptOperatorDec: { struct pfParse *name = pp->children; struct pfParse *input = name->next; struct pfParse *output = input->next; struct pfParse *body = output->next; struct pfParse *class = pfParseEnclosingClass(pp->parent); name->type = pptSymName; pp->ty->access = pp->access; pp->ty->isConst = pp->isConst; pp->var = pfScopeAddVar(pp->scope->parent, name->name, pp->ty, pp); if (class != NULL) { struct pfType *classType = class->children->ty; struct pfBaseType *classBase = classType->base; pfScopeAddVar(pp->scope, "self", classType, class); if (pfBaseIsDerivedClass(classBase)) { struct pfBaseType *parentBase = classBase->parent; pfScopeAddVar(pp->scope, "parent", pfTypeNew(parentBase), class); } if (sameString(name->name, "init")) { struct pfBaseType *classBase = classType->base; classBase->initMethod = pp; checkNoOutput(pp); } } break; } case pptInclude: { struct pfParse *name = pp->children; name->type = pptSymName; break; } } for (pp = pp->children; pp != NULL; pp = pp->next) addDeclaredVarsToScopes(pfc, pp); } static void linkInVars(struct hash *vars, struct pfScope *scope) /* Types hash is full of pfVar. Add these to scope. */ { struct hashCookie it = hashFirst(vars); struct hashEl *el; while ((el = hashNext(&it)) != NULL) { struct pfVar *var = el->val; enum pfAccessType access = var->ty->access; if (access == paGlobal || access == paWritable || access == paReadable) hashAdd(scope->vars, el->name, var); } } static void addIncludedSymbols(struct pfCompile *pfc, struct pfParse *pp, int level) /* Add symbols from included files. */ { if (pp->type == pptInclude) { struct pfModule *module; module = hashMustFindVal(pfc->moduleHash, pp->children->name); linkInVars(module->scope->vars, pp->scope); } level += 1; if (level <= 2) /* Include at pptProgram->pptModule->pptInclude, no deeper */ { for (pp = pp->children; pp != NULL; pp = pp->next) addIncludedSymbols(pfc, pp, level); } } static void rSetVarScope(struct pfParse *pp, struct pfScope *scope) /* Set scope for the variable to module in construct like: * module.variable * module.variable[index] * module.variable() */ { pp->scope = scope; switch (pp->type) { case pptDot: case pptIndex: case pptCall: rSetVarScope(pp->children, scope); break; case pptVarUse: pp->scope = scope; break; } } static void checkVarsDefined(struct pfCompile *pfc, struct pfParse *pp, struct pfModule *module) /* Attach variable name to pfVar in appropriate scope. * Complain and die if not found. */ { struct pfParse *p; switch (pp->type) { case pptModule: case pptModuleRef: case pptMainModule: module = pp->scope->module; break; case pptDot: { struct pfParse *leftOfDot = pp->children; struct pfParse *rightOfDot = leftOfDot->next; if (leftOfDot->type == pptNameUse) { char *name = leftOfDot->name; struct pfVar *var = pfScopeFindVar(leftOfDot->scope, name); if (var != NULL && var->ty->base == pfc->moduleType) { char *modName = var->parse->name; struct pfModule *module = pfScopeFindModule(pp->scope, modName); if (!module) internalErrAt(leftOfDot->tok); leftOfDot->type = pptModuleUse; rSetVarScope(rightOfDot, module->scope); } else { rightOfDot->type = pptFieldUse; } } else { rightOfDot->type = pptFieldUse; } break; } case pptNameUse: { struct pfVar *var = pfScopeFindVar(pp->scope, pp->name); if (var != NULL) { struct pfModule *varModule = var->scope->module; if (varModule != NULL && varModule != module) { enum pfAccessType access = var->ty->access; if (access != paGlobal && access != paWritable) { errAt(pp->tok, "%s is private to module %s", var->name, varModule->name); } } pp->var = var; pp->type = pptVarUse; pp->ty = var->ty; } else errAt(pp->tok, "%s used but not defined", pp->name); break; } } for (p = pp->children; p != NULL; p = p->next) checkVarsDefined(pfc, p, module); } void pfBindVars(struct pfCompile *pfc, struct pfParse *pp) /* pfBindVars - massage parse tree slightly to regularize * variable and function declarations. Then put all declarations * in the appropriate scope, and bind variable uses and function * calls to the appropriate declaration. This will complain * about undefined symbols. */ { evalDeclarationTypes(pfc, pp, 0); addDeclaredVarsToScopes(pfc, pp); addIncludedSymbols(pfc, pp, 0); checkVarsDefined(pfc, pp, NULL); }