/*****
* 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.
*****/
if (t->kind == ty_record) {
record* r= dynamic_cast<record*>(t);
assert(r);
assert(r->getLevel());
if (!r->getLevel()->getParent()) return false;// r is actually a module
if (!r->getLevel()->getParent()->getParent()) {
return true;// r is a top-level record, or all nestings are static
}
return false; // r is nested non-statically
}
if (t->kind == ty_function) {
function* f= dynamic_cast<function*>(t);
assert(f);
// Check the types of the result and all the parameters.
if (!usableInTemplate(f->result)) return false;
const signature& sig= *f->getSignature();
for (const types::formal& f : sig.formals) {
if (!usableInTemplate(f.t)) return false;
}
if (sig.hasRest() && !usableInTemplate(sig.getRest().t)) return false;
return true;
}
if (t->kind == ty_array) {
array* a= dynamic_cast<array*>(t);
assert(a);
return usableInTemplate(a->celltype);
}
// We should have already handled all the cases.
assert(false);
return false;
}
trans::tyEntry *astType::transAsTyEntry(coenv &e, record *where)
{
return new trans::tyEntry(trans(e, false), nullptr, where, getPos());
}
void nameTy::prettyprint(ostream &out, Int indent)
{
prettyname(out, "nameTy",indent, getPos());
id->prettyprint(out, indent+1);
}
void addNameOps(coenv &e, record *r, record *qt, varEntry *qv, position pos) {
for (auto au : qt->e.ve.getAutoUnravels()) {
symbol auName = au.first;
varEntry *v = au.second;
if (!v->checkPerm(READ, e.c)) {
em.error(pos);
em << "cannot access '" << auName << "' in current scope";
continue;
}
if (r && e.c.getPermission() != PUBLIC) {
// Add an additional restriction to v based on c.getPermission().
v = new varEntry(*v, e.c.getPermission(), r);
}
varEntry *qqv = qualifyVarEntry(qv, v);
auto enter= [&](trans::venv& ve) {
// Add op only if it does not already exist.
if (!ve.lookByType(auName, qqv->getType())) {
ve.enter(auName, qqv);
}
};
if (r) {
enter(r->e.ve);
}
enter(e.e.ve);
}
}
// NOTE: Can this be merged with trans somehow?
void arrayTy::addOps(coenv &e, record *r, AutounravelOption)
{
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<types::array *>(t);
assert(at);
e.e.addArrayOps(at);
if (r)
r->e.addArrayOps(at);
}
}
void block::prettystms(ostream &out, Int indent)
{
for (list<runnable *>::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<runnable *>::iterator p = stms.begin(); p != stms.end(); ++p) {
(*p)->markTrans(e);
}
}
void block::transAsField(coenv &e, record *r)
{
Scope scopeHolder(e, scope);
for (list<runnable *>::iterator p = stms.begin(); p != stms.end(); ++p) {
(*p)->markTransAsField(e, r);
if (em.errors() && !settings::debug)
break;
}
}
auto p = stms.begin();
// Start with second statement since the first was a receiveTypedefDec.
while (++p != stms.end()) {
(*p)->markTransAsField(e, r);
if (em.errors() && !settings::debug) {
return false;
}
}
em.sync();
return true;
}
receiveTypedefDec* block::getTypedefDec()
{
auto p= stms.begin();
// Check for an empty file
if (p == stms.end()) return nullptr;
return dynamic_cast<receiveTypedefDec*>(*p);
}
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);
if (settings::getSetting<bool>("autoplain")) {
autoplainRunnable()->transAsField(ce, r);
}
// If the file starts with a template declaration and it was accessed
// or imported without template arguments, further translation is
// likely futile.
if (getTypedefDec() != nullptr) {
if(!settings::getSetting<bool>("listvariables")) {
em.error(getPos());
em << "templated module access requires template parameters";
em.sync();
}
return r;
}
// Translate the abstract syntax.
transAsField(ce, r);
ce.c.closeRecord();
em.sync();
if (em.errors()) return nullptr;
return r;
}
record* block::transAsTemplatedFile(
genv& ge, symbol id, mem::vector<absyntax::namedTy*>* args
)
{
// 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);
// Import `plain` before even translating "typedef import" since the latter
// might change the meanings of symbols provided by `plain`.
if (settings::getSetting<bool>("autoplain")) {
autoplainRunnable()->transAsField(ce, r);
}
// Translate the abstract syntax.
bool succeeded = transAsTemplatedField(ce, r, args);
ce.c.closeRecord();
if (!succeeded) {
return nullptr;
}
return r;
}
bool block::returns() {
// Search for a returning runnable, starting at the end for efficiency.
for (list<runnable *>::reverse_iterator p=stms.rbegin();
p != stms.rend();
++p)
if ((*p)->returns())
return true;
return false;
}
vardec *block::asVardec()
{
vardec *var = 0;
for (list<runnable *>::iterator p=stms.begin();
p != stms.end();
++p)
{
vardec *v = dynamic_cast<vardec *>(*p);
if (v) {
if (var)
// Multiple vardecs.
return 0;
var = v;
}
else if (!dynamic_cast<emptyStm *>(*p))
// Failure due to another runnable in the block.
return 0;
}
void modifierList::prettyprint(ostream &out, Int indent)
{
prettyindent(out,indent);
out << "modifierList (";
for (list<modifier>::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<permission>::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";
}
}
void modifiedRunnable::transAsField(coenv &e, record *r)
{
if (mods->staticSet()) {
modifier mod = mods->getModifier();
if (e.c.isTopLevel()) {
if (mod == AUTOUNRAVEL) {
em.error(getPos());
em << "top-level fields cannot be autounraveled";
return;
} else {
em.warning(getPos());
em << "static modifier is meaningless at top level";
}
}
e.c.pushModifier(mod);
}
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 (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), nullptr, 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<types::function *>(getType(base, e, true));
assert(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);
}
// 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(nullPos, 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);
if (e.c.isAutoUnravel()) {
r->e.ve.registerAutoUnravel(id, v);
}
}
e.e.addVar(id, v);
}
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";
}
// 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;
// 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;
bool tainted=settings::debug && em.errors();
record *imp=e.e.getModule(id, filename);
if (!imp) {
if(!tainted) {
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, 0)'.
callExp init(
pos, new loadModuleExp(pos, imp), new stringExp(pos, filename),
new intExp(pos, 0)
);
// 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;
}
}
// Returns the number of items that will be added to the stack once the
// translation has been run (always either 0 or 1).
Int transPushParent(formal *f, coenv &e) {
if (f->getType(e)->kind != types::ty_record) {
return 0;
}
astType *astT = f->getAbsyntaxType();
tyEntry *ent = astT->transAsTyEntry(e, nullptr);
bool succeeded = e.c.encodeParent(f->getPos(), ent);
if (!succeeded) {
em.compiler(f->getPos());
em << "failed to encode parent level";
em.sync(true);
}
return 1;
}
// Translates formals into namedTys.
mem::vector<namedTy*> *computeTemplateArgs(formals *args, coenv &e) {
auto *computedArgs = new mem::vector<namedTy*>();
for (formal *f : *args) {
symbol theName = f->getName();
position sourcePos = f->getPos();
if (theName == symbol::nullsym) {
em.error(sourcePos);
em << "expected typename=";
em.sync(true);
return nullptr;
}
types::ty *t = f->getType(e);
if (!usableInTemplate(t)) {
em.error(f->getAbsyntaxType()->getPos());
em << "non-statically nested types cannot be used in templates";
em.sync(true);
return nullptr;
}
computedArgs->push_back(new namedTy(sourcePos, theName, t));
}
return computedArgs;
}
// 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 moduleName=(string) id;
record *imp=e.e.getTemplatedModule(moduleName,computedArgs);
if (!imp) {
em.error(pos);
em << "could not load module '" << id << "'";
em.sync(true);
return nullptr;
}
// Encode action: Push parents to the stack.
Int numParents = 0;
// We push parents in reverse order so that we can later pop them in
// order, meaning that if more than one parameter has an error, the first
// error will be reported rather than the last.
for (auto p = args->rbegin(); p != args->rend(); ++p) {
numParents += transPushParent(*p, e);
}
// Create a varinit that evaluates to the module.
// This is effectively the expression 'loadModule(index, numParents)'.
callExp init(
pos, new loadModuleExp(pos, imp),
new stringExp(pos, imp->getTemplateIndex()),
new intExp(pos, numParents)
);
// The varEntry should have whereDefined()==nullptr as it is not defined
// inside the record r.
varEntry *v=makeVarEntryWhere(e, r, imp, nullptr, 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 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 *r, position here)
{
// This is almost equivalent to the code
// autounravel A operator init() { return new A; }
// where A is the name of the record. The "almost" is because the code below
// does not add the operator init to the environment, since in practice it
// would be added to the outer environment, not the record's environment.
// Since it is always added *after* any code in the record, we lose nothing
// by not adding it to the environment.
// Additionally, the "autounravel" is made low-priority (will not override
// user-defined operator init) which is possible only for built-in functions.
formals formals(here);
simpleName recordName(here, id);
nameTy result(here, &recordName);
newRecordExp exp(here, &result);
returnStm stm(here, &exp);
fundef fun(here, &result, &formals, &stm);
assert(r);
{
e.c.pushModifier(AUTOUNRAVEL);
function *ft = fun.transType(e, false);
assert(ft);
symbol initSym=symbol::opTrans("init");
varinit *init=fun.makeVarInit(ft);
assert(ft->kind != types::ty_inferred);
varEntry *v=makeVarEntry(here, e, r, ft);
r->e.addVar(initSym, v);
r->e.ve.registerAutoUnravel(initSym, v, trans::AutounravelPriority::OFFER);
initializeVar(here, e, v, init);
e.c.popModifier();
}
}
bool typeParam::transAsParamMatcher(coenv &e, record *module, namedTy* arg) {
symbol name = arg->dest;
ty *t = arg->t;
if (name != paramSym) {
em.error(arg->pos);
em << "template argument name does not match module: passed "
<< name
<< ", expected "
<< paramSym;
return false;
}
if (t->kind != types::ty_record) {
tyEntry *ent = new tyEntry(t, nullptr, module, getPos());
addTypeWithPermission(e, module, ent, name);
return true;
}
// We can now assume t is a record (i.e., asy struct).
record *r = dynamic_cast<record *>(t);
assert(r);
//
// Build a varEntry v for the parent level and encode the bytecode
// to pop the parent off the stack into v. The varEntry needs a type,
// but the only thing we use from the type is the level, so make a
// new fake record.
string fakeParentName;
# ifdef DEBUG_FRAME
{
ostringstream oss;
oss << "<fake parent of " << name << " holding ";
print(oss, r->getLevel()->getParent());
oss << ">";
fakeParentName = oss.str();
}
# else
fakeParentName = "<fake parent of " + static_cast<string>(name) + ">";
# endif
record* fakeParent = new record(
symbol::literalTrans(fakeParentName), r->getLevel()->getParent()
);
varEntry *v = makeVarEntryWhere(e, module, fakeParent, nullptr, getPos());
// Encode bytecode to pop the parent off the stack into v.
v->encode(WRITE, getPos(), e.c);
e.c.encodePop();
//
// Build tyEntry, using v as ent->v.
tyEntry *ent = new tyEntry(t, v, module, getPos());
addTypeWithPermission(e, module, ent, name);
// Add any autounravel fields.
addNameOps(e, module, r, v, getPos());
return true;
}
void typeParamList::prettyprint(ostream &out, Int indent) {
for (auto p = params.begin(); p != params.end(); ++p) {
(*p)->prettyprint(out, indent);
}
}
// RAII class to set the permission of a coder to a new value, and then reset it
// to the old value when the object goes out of scope.
class PermissionSetter {
coder &c;
permission oldPerm;
public:
PermissionSetter(coder &c, permission newPerm) : c(c), oldPerm(c.getPermission()) {
c.setPermission(newPerm);
}
~PermissionSetter() {
c.setPermission(oldPerm);
}
};
bool typeParamList::transAsParamMatcher(
coenv &e, record *r, mem::vector<namedTy*> *args
) {
// Check that the number of arguments passed matches the number of parameters.
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;
}
// Set the permission to PRIVATE while translating type parameters.
PermissionSetter ps(e.c, PRIVATE);
// Pop the parents off the stack. They were pushed in reverse order.
// With this approach, the first error will be reported, rather than the last.
for (size_t i = 0; i < params.size(); ++i) {
bool succeeded = params[i]->transAsParamMatcher(e, r, (*args)[i]);
if (!succeeded) return false;
}
return true;
}
bool receiveTypedefDec::transAsParamMatcher(
coenv& e, record *r, mem::vector<namedTy*> *args
) {
bool succeeded = params->transAsParamMatcher(e, r, args);
return succeeded;
}
void receiveTypedefDec::transAsField(coenv& e, record *r) {
em.error(getPos());
em << "unexpected 'typedef import'";
em.sync();
}
fromdec::qualifier unraveldec::getQualifier(coenv &e, record *)
{
// getType is where errors in the qualifier are reported.
record *qt=dynamic_cast<record *>(id->getType(e, false));
if (!qt) {
em.error(getPos());
em << "qualifier is not a record";
}
return qualifier(qt,id->getVarEntry(e));
}
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<record *>(v->getType());
if (!qt) {
em.compiler(getPos());
em << "qualifier is not a record";
}
return qualifier(qt, v);
}
else
return qualifier(0,0);
}
// The runnables will be translated, one at a time, without any additional
// scoping.
ast->transAsField(e, r);
}
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);
// Add the autounravel fields also.
addNameOps(e, parent, r, nullptr, getPos());
}
void recorddec::transAsField(coenv &e, record *parent)
{
if (e.c.isAutoUnravel()) {
em.error(getPos());
em << "types cannot be autounraveled";
}
record *r = parent ? parent->newRecord(id, e.c.isStatic()) :
e.c.newRecord(id);
addTypeWithPermission(
e, parent, new trans::tyEntry(r, nullptr, parent, getPos()), id
);
trans::addRecordOps(r);
// Start translating the initializer.
coder c=e.c.newRecordInit(getPos(), r);
coenv re(c,e.e);
{
// Make sure the autounraveled fields are limited to the record's scope.
// If the scope is too broad, then user-provide autounravel overrides will
// defer to already-defined ops when they should not.
bool useScope = body->scope;
// RAII: Close the scope when scopeHolder runs its destructor.
Scope scopeHolder(re, useScope);
// Autounravel the record ops into the record's environment.
addNameOps(re, nullptr, r, nullptr, getPos());
// We've already handled the scope ourselves, so tell `body` not to add an
// additional scope when running `transAsField`.
body->scope = false;
// Translate the main body of the record.
body->transAsField(re, r);
// Restore the original value of the `scope` boolean. This probably makes no
// difference but is included out of an abundance of caution.
body->scope = useScope;
} // Close the scope.
// 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(re, r);
// This would normally be done right after transAsField, but we needed to add
// the default initializer first.
re.c.closeRecord();
// 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", as well as the autounravel fields (both builtin and user
// defined).
addPostRecordEnvironment(e, r, parent);
}
runnable *autoplainRunnable() {
// Abstract syntax for the code:
// private import plain;
position pos=nullPos;
static importdec ap(pos, new idpair(pos, symbol::literalTrans("plain")));
static modifiedRunnable mr(pos, trans::PRIVATE, &ap);