/***** * dec.cc * Andy Hammerlindl 2002/8/29 * * Represents the abstract syntax tree for declarations in the language. * Also included is an abstract syntax for types as they are most often * used with declarations. *****/ #include "errormsg.h" #include "coenv.h" #include "dec.h" #include "fundec.h" #include "newexp.h" #include "stm.h" #include "exp.h" #include "modifier.h" #include "runtime.h" #include "locate.h" #include "asyparser.h" // #include "builtin.h" // for trans::addRecordOps namespace absyntax { using namespace trans; using namespace types; using mem::list; trans::tyEntry *astType::transAsTyEntry(coenv &e, record *where) { return new trans::tyEntry(trans(e, false), 0, where, getPos()); } void nameTy::prettyprint(ostream &out, Int indent) { prettyname(out, "nameTy",indent, getPos()); id->prettyprint(out, indent+1); } types::ty *nameTy::trans(coenv &e, bool tacit) { return id->typeTrans(e, tacit); } trans::tyEntry *nameTy::transAsTyEntry(coenv &e, record *) { return id->tyEntryTrans(e); } nameTy::operator string() const { return static_cast(id->getName()); } void dimensions::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "dimensions (" << depth << ")\n"; } types::array *dimensions::truetype(types::ty *base, bool tacit) { if (!tacit && base->kind == ty_void) { em.error(getPos()); em << "cannot declare array of type void"; } assert(depth >= 1); size_t d=depth; types::array *a=new types::array(base); d--; for (; d > 0; d--) { a = new types::array(a); } return a; } void arrayTy::prettyprint(ostream &out, Int indent) { prettyname(out, "arrayTy",indent, getPos()); cell->prettyprint(out, indent+1); dims->prettyprint(out, indent+1); } // NOTE: Can this be merged with trans somehow? void arrayTy::addOps(coenv &e, record *r) { types::ty *t=trans(e, true); // Only add ops if it is an array (and not, say, an error) if (t->kind == types::ty_array) { types::array *at=dynamic_cast(t); assert(at); e.e.addArrayOps(at); if (r) r->e.addArrayOps(at); } } types::ty *arrayTy::trans(coenv &e, bool tacit) { types::ty *ct = cell->trans(e, tacit); assert(ct); // Don't make an array of errors. if (ct->kind == types::ty_error) return ct; types::array *t = dims->truetype(ct,tacit); assert(t); return t; } arrayTy::operator string() const { stringstream ss; ss << static_cast(*cell); for (size_t i = 0; i < dims->size(); i++) { ss << "[]"; } return ss.str(); } tyEntryTy::tyEntryTy(position pos, types::ty *t) : astType(pos), ent(new trans::tyEntry(t, 0, 0, position())) { } void tyEntryTy::prettyprint(ostream &out, Int indent) { prettyindent(out,indent); out << "tyEntryTy: " << *(ent->t) << "\n"; } types::ty *tyEntryTy::trans(coenv &, bool) { return ent->t; } tyEntryTy::operator string() const { return ""; } vm::lambda *runnable::transAsCodelet(coenv &e) { coder c=e.c.newCodelet(getPos()); coenv ce(c, e.e); markTrans(ce); return c.close(); } void block::prettystms(ostream &out, Int indent) { for (list::iterator p = stms.begin(); p != stms.end(); ++p) (*p)->prettyprint(out, indent); } void block::prettyprint(ostream &out, Int indent) { prettyname(out,"block",indent,getPos()); prettystms(out, indent+1); } // Uses RAII to ensure scope is ended when function returns. class Scope { coenv* e; public: Scope(coenv &e, bool scope) : e(scope ? &e : nullptr) { if (this->e) this->e->e.beginScope(); } ~Scope() { if (this->e) e->e.endScope(); } }; void block::trans(coenv &e) { Scope scopeHolder(e, scope); for (list::iterator p = stms.begin(); p != stms.end(); ++p) { (*p)->markTrans(e); } } void block::transAsField(coenv &e, record *r) { Scope scopeHolder(e, scope); for (list::iterator p = stms.begin(); p != stms.end(); ++p) { (*p)->markTransAsField(e, r); if (em.errors() && !settings::getSetting("debug")) break; } } bool block::transAsTemplatedField( coenv &e, record *r, mem::vector* args, frame *caller ) { Scope scopeHolder(e, scope); auto p = stms.begin(); if (p == stms.end()) { return true; // empty file } receiveTypedefDec *dec = dynamic_cast(*p); if (!dec) { em.error(getPos()); em << "expected 'typedef import();'"; em.sync(true); return false; } if(!dec->transAsParamMatcher(e, r, args, caller)) return false; while (++p != stms.end()) { (*p)->markTransAsField(e, r); if (em.errors() && !settings::getSetting("debug")) { return false; } } em.sync(); return true; } void block::transAsRecordBody(coenv &e, record *r) { transAsField(e, r); e.c.closeRecord(); } bool block::transAsTemplatedRecordBody( coenv &e, record *r, mem::vector *args, frame *caller ) { bool succeeded = transAsTemplatedField(e, r, args, caller); e.c.closeRecord(); return succeeded; } record *block::transAsFile(genv& ge, symbol id) { // Create the new module. record *r = new record(id, new frame(id,0,0)); // Create coder and environment to translate the module. // File-level modules have dynamic fields by default. coder c(getPos(), r, 0); env e(ge); coenv ce(c, e); // Translate the abstract syntax. if (settings::getSetting("autoplain")) { autoplainRunnable()->transAsField(ce, r); } transAsRecordBody(ce, r); em.sync(); if (em.errors()) return nullptr; return r; } record *block::transAsTemplatedFile( genv& ge, symbol id, mem::vector* args, coenv& cE ) { for (auto p = args->rbegin(); p != args->rend(); ++p) { namedTyEntry *arg = *p; tyEntry *ent = arg->ent; if(ent->t->kind == types::ty_record) { varEntry *v = ent->v; if (v) { // Push the value of v to the stack. v->getLocation()->encode(READ, arg->pos, cE.c); } else { // Push the appropriate frame to the stack. newRecordExp::encodeLevel(arg->pos,cE,ent); } } } // Create the new module. record *r = new record(id, new frame(id, 0, 0)); // Create coder and environment to translate the module. // File-level modules have dynamic fields by default. coder c(getPos(), r, 0); env e(ge); coenv ce(c, e); // Translate the abstract syntax. if (settings::getSetting("autoplain")) { autoplainRunnable()->transAsField(ce, r); } bool succeeded = transAsTemplatedRecordBody(ce, r, args, cE.c.getFrame()); if (!succeeded) { return nullptr; } return r; } bool block::returns() { // Search for a returning runnable, starting at the end for efficiency. for (list::reverse_iterator p=stms.rbegin(); p != stms.rend(); ++p) if ((*p)->returns()) return true; return false; } vardec *block::asVardec() { vardec *var = 0; for (list::iterator p=stms.begin(); p != stms.end(); ++p) { vardec *v = dynamic_cast(*p); if (v) { if (var) // Multiple vardecs. return 0; var = v; } else if (!dynamic_cast(*p)) // Failure due to another runnable in the block. return 0; } return var; } void block::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP for (auto const& p : stms) { p->createSymMap(symContext); } #endif } void dec::prettyprint(ostream &out, Int indent) { prettyname(out, "dec", indent, getPos()); } void modifierList::prettyprint(ostream &out, Int indent) { prettyindent(out,indent); out << "modifierList ("; for (list::iterator p = mods.begin(); p != mods.end(); ++p) { if (p != mods.begin()) out << ", "; switch (*p) { case EXPLICIT_STATIC: out << "static"; break; #if 0 case EXPLICIT_DYNAMIC: out << "dynamic"; break; #endif default: out << "invalid code"; } } for (list::iterator p = perms.begin(); p != perms.end(); ++p) { if (p != perms.begin() || !mods.empty()) out << ", "; switch (*p) { case PUBLIC: out << "public"; break; case PRIVATE: out << "private"; break; default: out << "invalid code"; } } out << ")\n"; } bool modifierList::staticSet() { return !mods.empty(); } modifier modifierList::getModifier() { if (mods.size() > 1) { em.error(getPos()); em << "too many modifiers"; } assert(staticSet()); return mods.front(); } permission modifierList::getPermission() { if (perms.size() > 1) { em.error(getPos()); em << "too many modifiers"; } return perms.empty() ? DEFAULT_PERM : perms.front(); } void modifiedRunnable::prettyprint(ostream &out, Int indent) { prettyname(out, "modifierRunnable",indent, getPos()); mods->prettyprint(out, indent+1); body->prettyprint(out, indent+1); } void modifiedRunnable::transAsField(coenv &e, record *r) { if (mods->staticSet()) { if (e.c.isTopLevel()) { em.warning(getPos()); em << "static modifier is meaningless at top level"; } e.c.pushModifier(mods->getModifier()); } permission p = mods->getPermission(); #if 0 // This is innocuous if (p != DEFAULT_PERM && (!r || !body->allowPermissions())) { em.warning(pos); em << "permission modifier is meaningless"; } #endif e.c.setPermission(p); body->transAsField(e,r); e.c.clearPermission(); if (mods->staticSet()) e.c.popModifier(); } void decidstart::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "decidstart '" << id << "'\n"; if (dims) dims->prettyprint(out, indent+1); } types::ty *decidstart::getType(types::ty *base, coenv &, bool) { return dims ? dims->truetype(base) : base; } trans::tyEntry *decidstart::getTyEntry(trans::tyEntry *base, coenv &e, record *where) { return dims ? new trans::tyEntry(getType(base->t,e,false), 0, where, getPos()) : base; } void decidstart::addOps(types::ty *base, coenv &e, record *r) { if (dims) { array *a=dims->truetype(base); e.e.addArrayOps(a); if (r) r->e.addArrayOps(a); } } void decidstart::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP std::string name(static_cast(getName())); AsymptoteLsp::posInFile pos(getPos().LineColumn()); if (auto decCtx=dynamic_cast(symContext)) { decCtx->additionalDecs.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name, pos)); } else { symContext->symMap.varDec[name] = AsymptoteLsp::SymbolInfo(name, pos); } #endif } void decidstart::createSymMapWType( AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base ) { #ifdef HAVE_LSP std::string name(static_cast(getName())); AsymptoteLsp::posInFile pos(getPos().LineColumn()); if (auto decCtx=dynamic_cast(symContext)) { if (base == nullptr) { decCtx->additionalDecs.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name, pos)); } else { decCtx->additionalDecs.emplace( std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name, static_cast(*base), pos) ); } } else { symContext->symMap.varDec[name] = base == nullptr ? AsymptoteLsp::SymbolInfo(name, pos) : AsymptoteLsp::SymbolInfo(name, static_cast(*base), pos); } #endif } void fundecidstart::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "fundecidstart '" << id << "'\n"; if (dims) dims->prettyprint(out, indent+1); if (params) params->prettyprint(out, indent+1); } types::ty *fundecidstart::getType(types::ty *base, coenv &e, bool tacit) { types::ty *result = decidstart::getType(base, e, tacit); if (params) { return params->getType(result, e, true, tacit); } else { types::ty *t = new function(base); return t; } } trans::tyEntry *fundecidstart::getTyEntry(trans::tyEntry *base, coenv &e, record *where) { return new trans::tyEntry(getType(base->t,e,false), 0, where, getPos()); } void fundecidstart::addOps(types::ty *base, coenv &e, record *r) { decidstart::addOps(base, e, r); params->addOps(e, r); types::function *ft=dynamic_cast(getType(base, e, true)); assert(ft); e.e.addFunctionOps(ft); if (r) r->e.addFunctionOps(ft); } void decid::prettyprint(ostream &out, Int indent) { prettyname(out, "decid",indent, getPos()); start->prettyprint(out, indent+1); if (init) init->prettyprint(out, indent+1); } varEntry *makeVarEntryWhere(coenv &e, record *r, types::ty *t, record *where, position pos) { access *a = r ? r->allocField(e.c.isStatic()) : e.c.allocLocal(); return r ? new varEntry(t, a, e.c.getPermission(), r, where, pos) : new varEntry(t, a, where, pos); } varEntry *makeVarEntry(position pos, coenv &e, record *r, types::ty *t) { return makeVarEntryWhere(e, r, t, r, pos); } // Defined in constructor.cc. bool definesImplicitConstructor(coenv &e, record *r, varEntry *v, symbol id); void addConstructorFromInitializer(position pos, coenv &e, record *r, varEntry *init); void addVar(coenv &e, record *r, varEntry *v, symbol id) { // Test for 'operator init' definitions that implicitly define constructors: if (definesImplicitConstructor(e, r, v, id)) addConstructorFromInitializer(position(), e, r, v); // Add to the record so it can be accessed when qualified; add to the // environment so it can be accessed unqualified in the scope of the // record definition. if (r) r->e.addVar(id, v); e.e.addVar(id, v); } void initializeVar(position pos, coenv &e, varEntry *v, varinit *init) { types::ty *t=v->getType(); if (init) init->transToType(e, t); else { definit d(pos); d.transToType(e, t); } v->getLocation()->encode(WRITE, pos, e.c); e.c.encodePop(); } types::ty *inferType(position pos, coenv &e, varinit *init) { if (!init) { em.error(pos); em << "inferred variable declaration without initializer"; return primError(); } exp *base = dynamic_cast(init); bool Void=false; if (base) { types::ty *t = base->cgetType(e); Void=t->kind == ty_void; if (t->kind != ty_overloaded && !Void) return t; } em.error(pos); em << (Void ? "cannot infer from void" : "could not infer type of initializer"); return primError(); } void createVar(position pos, coenv &e, record *r, symbol id, types::ty *t, varinit *init) { // I'm not sure how to handle inferred types in these cases. assert(t->kind != types::ty_inferred); varEntry *v=makeVarEntry(pos, e, r, t); addVar(e, r, v, id); initializeVar(pos, e, v, init); } void createVarOutOfOrder(position pos, coenv &e, record *r, symbol id, types::ty *t, varinit *init) { /* For declarations such as "var x = 5;", infer the type from the * initializer. */ if (t->kind == types::ty_inferred) t = inferType(pos, e, init); varEntry *v=makeVarEntry(pos, e, r, t); initializeVar(pos, e, v, init); addVar(e, r, v, id); } void addTypeWithPermission(coenv &e, record *r, tyEntry *base, symbol id) { // Only bother encoding permissions for private types. tyEntry *ent = (r && e.c.getPermission()==PRIVATE) ? new trans::tyEntry(base, PRIVATE, r) : base; if (r) r->e.addType(id, ent); e.e.addType(id, ent); } void decid::transAsField(coenv &e, record *r, types::ty *base) { types::ty *t = start->getType(base, e); assert(t); if (t->kind == ty_void) { em.error(getPos()); em << "cannot declare variable of type void"; } start->addOps(base, e, r); createVarOutOfOrder(getPos(), e, r, start->getName(), t, init); } void decid::transAsTypedefField(coenv &e, trans::tyEntry *base, record *r) { trans::tyEntry *ent = start->getTyEntry(base, e, r); assert(ent && ent->t); if (init) { em.error(getPos()); em << "type definition cannot have initializer"; } start->addOps(base->t, e, r); addTypeWithPermission(e, r, ent, start->getName()); } void decid::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP start->createSymMap(symContext); if (init) { init->createSymMap(symContext); } #endif } void decid::createSymMapWType( AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base ) { #ifdef HAVE_LSP start->createSymMapWType(symContext, base); if (init) { init->createSymMap(symContext); } #endif } void decidlist::prettyprint(ostream &out, Int indent) { prettyname(out, "decidlist",indent, getPos()); for (list::iterator p = decs.begin(); p != decs.end(); ++p) (*p)->prettyprint(out, indent+1); } void decidlist::transAsField(coenv &e, record *r, types::ty *base) { for (list::iterator p = decs.begin(); p != decs.end(); ++p) (*p)->transAsField(e, r, base); } void decidlist::transAsTypedefField(coenv &e, trans::tyEntry *base, record *r) { for (list::iterator p = decs.begin(); p != decs.end(); ++p) (*p)->transAsTypedefField(e, base, r); } void decidlist::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP for (auto const& p : decs) { p->createSymMap(symContext); } #endif } void decidlist::createSymMapWType( AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base ) { #ifdef HAVE_LSP for (auto const& p : decs) { p->createSymMapWType(symContext, base); } #endif } void vardec::prettyprint(ostream &out, Int indent) { prettyname(out, "vardec",indent, getPos()); base->prettyprint(out, indent+1); decs->prettyprint(out, indent+1); } void vardec::transAsTypedefField(coenv &e, record *r) { base->addOps(e, r); decs->transAsTypedefField(e, base->transAsTyEntry(e, r), r); } symbol vardec::singleName() { decid *did = decs->singleEntry(); if (!did) return symbol::nullsym; return did->getStart()->getName(); } types::ty *vardec::singleGetType(coenv &e) { decid *did = decs->singleEntry(); if (!did) return 0; return did->getStart()->getType(base->trans(e), e); } void vardec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP decs->createSymMapWType(symContext, base); #endif } // Helper class for imports. This essentially evaluates to the run::loadModule // function. However, that function returns different types of records // depending on the filename given to it, so we cannot add it to the // environment. Instead, we explicitly tell it what types::record it is // returning for each use. class loadModuleExp : public exp { function *ft; public: loadModuleExp(position pos, record *imp) : exp(pos) {ft=new function(imp,primString());} void prettyprint(ostream &out, Int indent) { prettyname(out, "loadModuleExp", indent, getPos()); } types::ty *trans(coenv &) { em.compiler(getPos()); em << "trans called for loadModuleExp"; return primError(); } void transCall(coenv &e, types::ty *t) { assert(equivalent(t, ft)); e.c.encode(inst::builtin, run::loadModule); } types::ty *getType(coenv &) { return ft; } exp *evaluate(coenv &, types::ty *) { // Don't alias. return this; } }; // Creates a local variable to hold the import and translate the accessing of // the import, but doesn't add the import to the environment. varEntry *accessModule(position pos, coenv &e, record *r, symbol id) { string filename=(string) id; record *imp=e.e.getModule(id, filename); if (!imp) { em.error(pos); em << "could not load module '" << filename << "'"; em.sync(true); return 0; } else { // Create a varinit that evaluates to the module. // This is effectively the expression 'loadModule(filename)'. callExp init(pos, new loadModuleExp(pos, imp), new stringExp(pos, filename)); // The varEntry should have whereDefined()==0 as it is not defined inside // the record r. varEntry *v=makeVarEntryWhere(e, r, imp, 0, pos); initializeVar(pos, e, v, &init); return v; } } // Creates a local variable to hold the import and translate the accessing of // the import, but doesn't add the import to the environment. varEntry *accessTemplatedModule(position pos, coenv &e, record *r, symbol id, formals *args) { string filename=(string) id; stringstream buf; buf << id << '/' << args->getSignature(e)->handle() << '/'; symbol index=symbol::literalTrans(buf.str()); auto *computedArgs = new mem::vector(); mem::vector *fields = args->getFields(); for (auto p = fields->begin(); p != fields->end(); ++p) { astType* theType = p->ty; symbol theName = p->sym; if (theName == symbol::nullsym) { em.error(theType->getPos()); em << "expected typename="; em.sync(true); return nullptr; } computedArgs->push_back(new namedTyEntry( theType->getPos(), theName, theType->transAsTyEntry(e, r) )); } record *imp=e.e.getTemplatedModule(index,filename,computedArgs,e); if (!imp) { em.error(pos); em << "could not load module '" << id << "'"; em.sync(true); return nullptr; } else { // Create a varinit that evaluates to the module. // This is effectively the expression 'loadModule(index)'. callExp init(pos, new loadModuleExp(pos, imp), new stringExp(pos, index)); // The varEntry should have whereDefined()==0 as it is not defined inside // the record r. varEntry *v=makeVarEntryWhere(e, r, imp, 0, pos); initializeVar(pos, e, v, &init); return v; } } void idpair::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "idpair (" << "'" << src << "' as " << dest << ")\n"; } void idpair::transAsAccess(coenv &e, record *r) { checkValidity(); varEntry *v=accessModule(getPos(), e, r, src); if (v) addVar(e, r, v, dest); } void idpair::transAsUnravel(coenv &e, record *r, protoenv &source, varEntry *qualifier) { checkValidity(); if (r) r->e.add(src, dest, source, qualifier, e.c); if (!e.e.add(src, dest, source, qualifier, e.c)) { em.error(getPos()); em << "no matching types or fields of name '" << src << "'"; } } void idpair::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP if (valid) { string fullSrc(settings::locateFile(src, true)); if (not AsymptoteLsp::isVirtualFile((std::string)(fullSrc.c_str()))) { if (not fullSrc.empty()) { symContext->addEmptyExtRef((std::string)(fullSrc.c_str())); } // add (dest, source) to reference map. auto s = symContext->extRefs.fileIdPair.emplace( dest, (std::string) fullSrc.c_str() ); auto it=std::get<0>(s); auto success=std::get<1>(s); if (not success) { it->second = (std::string)(fullSrc.c_str()); } symContext->extRefs.addAccessVal(static_cast(dest)); } } #endif } void idpairlist::prettyprint(ostream &out, Int indent) { for (list::iterator p=base.begin(); p != base.end(); ++p) (*p)->prettyprint(out, indent); } void idpairlist::transAsAccess(coenv &e, record *r) { for (list::iterator p=base.begin(); p != base.end(); ++p) (*p)->transAsAccess(e,r); } void idpairlist::transAsUnravel(coenv &e, record *r, protoenv &source, varEntry *qualifier) { for (list::iterator p=base.begin(); p != base.end(); ++p) (*p)->transAsUnravel(e,r,source,qualifier); } void idpairlist::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP for (auto& idp : base) { idp->createSymMap(symContext); } #endif } idpairlist * const WILDCARD = 0; void accessdec::prettyprint(ostream &out, Int indent) { prettyname(out, "accessdec", indent, getPos()); base->prettyprint(out, indent+1); } void accessdec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP base->createSymMap(symContext); #endif } void templateAccessDec::transAsField(coenv& e, record* r) { if (!this->checkValidity()) return; args->addOps(e, r); varEntry *v=accessTemplatedModule(getPos(), e, r, this->src, args); if (v) addVar(e, r, v, dest); } void typeParam::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "typeParam (" << paramSym << ")\n"; } void recordInitializer(coenv &e, symbol id, record *parent, position here) { // This is equivalent to the code // A operator init() { return new A; } // where A is the name of the record. formals formals(here); simpleName recordName(here, id); nameTy result(here, &recordName); newRecordExp exp(here, &result); returnStm stm(here, &exp); fundec init(here, &result, symbol::opTrans("init"), &formals, &stm); init.transAsField(e, parent); } bool typeParam::transAsParamMatcher(coenv &e, record *r, namedTyEntry* arg) { position pos=arg->pos; if (arg->dest != paramSym) { em.error(arg->pos); em << "template argument name does not match module: passed " << arg->dest << ", expected " << paramSym; return false; } if(arg->ent->t->kind == types::ty_record) { record *module = dynamic_cast(arg->ent->v->getType()); symbol Module=symbol::literalTrans(module->getName()); record *imp=e.e.getLoadedModule(Module); tyEntry *entry; if(imp) { callExp init(pos, new loadModuleExp(pos, imp), new stringExp(pos, module->getName())); varEntry *v=makeVarEntryWhere(e, r, imp, 0, pos); initializeVar(pos, e, v, &init); if (v) addVar(e, r, v, Module); record *src = dynamic_cast(arg->ent->t); qualifiedName *qn=new qualifiedName( pos, new simpleName(pos,module->getName()), src->getName() ); entry=nameTy(pos,qn).transAsTyEntry(e, r); } else { entry=arg->ent; } addTypeWithPermission(e, r, entry, paramSym); recordInitializer(e,paramSym,r,pos); } else addTypeWithPermission(e, r, arg->ent, paramSym); //e.e.addType(paramSym, arg->ent); // The code below would add e.g. operator== to the context, but potentially // ignore overrides of operator==: // // types::astType *t = arg.ent->t; // if (t->kind == types::ty_record) { // record *r = dynamic_cast(t); // if (r) { // trans::addRecordOps(e.e.ve, r); // } // } else if (t->kind == types::ty_array) { // array *a = dynamic_cast(t); // if (a) { // trans::addArrayOps(e.e.ve, a); // } // } else if (t->kind == types::ty_function) { // function *f = dynamic_cast(t); // if (f) { // trans::addFunctionOps(e.e.ve, f); // } // } return true; } void typeParamList::prettyprint(ostream &out, Int indent) { for (auto p = params.begin(); p != params.end(); ++p) { (*p)->prettyprint(out, indent); } } void typeParamList::add(typeParam *tp) { params.push_back(tp); } bool typeParamList::transAsParamMatcher( coenv &e, record *r, mem::vector *args, frame *caller ) { if (args->size() != params.size()) { position pos = getPos(); if (args->size() >= 1) { pos = (*args)[0]->pos; } em.error(pos); if (args->size() > params.size()) { em << "too many types passed: got " << args->size() << ", expected " << params.size(); } else { em << "too few types passed: got " << args->size() << ", expected " << params.size(); } return false; } mem::vector *qualifiedArgs = new mem::vector(); const string callerContextName="callerContext/"; const static symbol *id0=new symbol(symbol::literalTrans(callerContextName)); record *callerContext = new record(*id0, caller); for (namedTyEntry *arg : *args) { if (arg->ent->t->kind == types::ty_record) { varEntry *v = arg->ent->v; varEntry *newV = makeVarEntryWhere(e, r, v ? v->getType() : callerContext, nullptr, arg->pos); newV->getLocation()->encode(WRITE, arg->pos, e.c); e.c.encodePop(); tyEntry *newEnt = qualifyTyEntry(newV, arg->ent); qualifiedArgs->push_back( new namedTyEntry(arg->pos, arg->dest, newEnt) ); } else { qualifiedArgs->push_back(arg); } } for (size_t i = 0; i < params.size(); ++i) { bool succeeded = params[i]->transAsParamMatcher(e, r, (*qualifiedArgs)[i]); if (!succeeded) return false; } return true; } symbol intSymbol() { const static symbol* intSymbol = new symbol(symbol::literalTrans("int")); return *intSymbol; } symbol templatedSymbol() { const static symbol* templatedSymbol = new symbol(symbol::literalTrans("/templated")); return *templatedSymbol; } bool receiveTypedefDec::transAsParamMatcher( coenv& e, record *r, mem::vector *args, frame *caller ) { bool succeeded = params->transAsParamMatcher(e, r, args, caller); types::ty *intTy = e.e.lookupType(intSymbol()); assert(intTy); e.e.addVar(templatedSymbol(), makeVarEntryWhere(e, nullptr, intTy, r, getPos()) ); return succeeded; } void receiveTypedefDec::transAsField(coenv& e, record *r) { em.error(getPos()); types::ty *intTy = e.e.lookupType(intSymbol()); assert(intTy); if (e.e.lookupVarByType(templatedSymbol(), intTy)) { em << "'typedef import()' must precede any other code"; } else { em << "templated module access requires template parameters"; } em.sync(true); } void fromdec::prettyprint(ostream &out, Int indent) { prettyname(out, "fromdec", indent, getPos()); fields->prettyprint(out, indent+1); } void fromdec::transAsField(coenv &e, record *r) { qualifier q=getQualifier(e,r); if (q.t) { if (fields==WILDCARD) { if (r) r->e.add(q.t->e, q.v, e.c); e.e.add(q.t->e, q.v, e.c); } else fields->transAsUnravel(e, r, q.t->e, q.v); } } void unraveldec::prettyprint(ostream &out, Int indent) { prettyname(out, "unraveldec", indent, getPos()); id->prettyprint(out, indent+1); idpairlist *f=this->fields; if(f) f->prettyprint(out, indent+1); } fromdec::qualifier unraveldec::getQualifier(coenv &e, record *) { // getType is where errors in the qualifier are reported. record *qt=dynamic_cast(id->getType(e, false)); if (!qt) { em.error(getPos()); em << "qualifier is not a record"; } return qualifier(qt,id->getVarEntry(e)); } void unraveldec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP std::string fileName = static_cast(id->getName()); if (not AsymptoteLsp::isVirtualFile(fileName)) { symContext->extRefs.addUnravelVal(fileName); } #endif } void fromaccessdec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "fromaccessdec '" << id << "'\n"; idpairlist *f=this->fields; if(f) f->prettyprint(out, indent+1); } fromdec::qualifier fromaccessdec::getQualifier(coenv &e, record *r) { varEntry *v = 0; if (templateArgs) { v = accessTemplatedModule(getPos(), e, r, id, templateArgs); } else { v=accessModule(getPos(), e, r, id); } if (v) { record *qt=dynamic_cast(v->getType()); if (!qt) { em.compiler(getPos()); em << "qualifier is not a record"; } return qualifier(qt, v); } else return qualifier(0,0); } void fromaccessdec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP // filename is id; std::string idStr(id); symContext->extRefs.addEmptyExtRef(idStr); auto* f=this->fields; if (f) { // add [dest] -> [src, filename] to fromAccessDecls; f->processListFn( [&symContext, &idStr](symbol const& src, symbol const& dest) { std::string srcId(src); std::string destId(dest); symContext->extRefs.addFromAccessVal(idStr, srcId, destId); }); } #endif } void importdec::prettyprint(ostream &out, Int indent) { prettyname(out, "importdec", indent, getPos()); base.prettyprint(out, indent+1); } void importdec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP base.createSymMap(symContext); #endif } void includedec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "includedec ('" << filename << "')\n"; } void includedec::loadFailed(coenv &) { em.warning(getPos()); em << "could not parse file of name '" << filename << "'"; em.sync(true); } void includedec::transAsField(coenv &e, record *r) { file *ast = parser::parseFile(filename,"Including"); em.sync(); // The runnables will be translated, one at a time, without any additional // scoping. ast->transAsField(e, r); } void includedec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP std::string fullname( (std::string) settings::locateFile(filename, true).c_str() ); if (not AsymptoteLsp::isVirtualFile(fullname)) { symContext->addEmptyExtRef(fullname); symContext->extRefs.includeVals.emplace(fullname); } #endif } void typedec::prettyprint(ostream &out, Int indent) { prettyname(out, "typedec",indent, getPos()); body->prettyprint(out, indent+1); } void recorddec::prettyprint(ostream &out, Int indent) { prettyindent(out, indent); out << "structdec '" << id << "'\n"; body->prettyprint(out, indent+1); } void recorddec::transRecordInitializer(coenv &e, record *parent) { recordInitializer(e,id,parent,getPos()); } void recorddec::addPostRecordEnvironment(coenv &e, record *r, record *parent) { if (parent) parent->e.add(r->postdefenv, 0, e.c); e.e.add(r->postdefenv, 0, e.c); } void recorddec::transAsField(coenv &e, record *parent) { record *r = parent ? parent->newRecord(id, e.c.isStatic()) : e.c.newRecord(id); addTypeWithPermission(e, parent, new trans::tyEntry(r,0,parent,getPos()), id); e.e.addRecordOps(r); if (parent) parent->e.addRecordOps(r); // Start translating the initializer. coder c=e.c.newRecordInit(getPos(), r); coenv re(c,e.e); body->transAsRecordBody(re, r); // After the record is translated, add a default initializer so that a // variable of the type of the record is initialized to a new instance by // default. transRecordInitializer(e, parent); // Add types and variables defined during the record that should be added to // the enclosing environment. These are the implicit constructors defined by // "operator init". addPostRecordEnvironment(e, r, parent); } void recorddec::createSymMap(AsymptoteLsp::SymbolContext* symContext) { #ifdef HAVE_LSP auto* newCtx = symContext->newContext(getPos().LineColumn()); auto* structTyInfo = symContext->newTypeDec( static_cast(id), getPos().LineColumn()); if (structTyInfo != nullptr) { structTyInfo->ctx = newCtx; body->createSymMap(newCtx); } else { cerr << "Cannot create new struct context" << endl; } #endif } runnable *autoplainRunnable() { // Abstract syntax for the code: // private import plain; position pos=position(); static importdec ap(pos, new idpair(pos, symbol::literalTrans("plain"))); static modifiedRunnable mr(pos, trans::PRIVATE, &ap); return &mr; } } // namespace absyntax