#include<bits/stdc++.h>
#define int long long
#define il inline
#define rt return
#define ope operator
using namespace std;
il int read()
{
int x=0,f=1;
char c=getchar();
while(!isdigit(c))
{
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c))
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
rt x*f;
}
il void write(int x)
{
if(x<0)
{
putchar('-');
write(-x);
rt;
}
if(x/10) write(x/10);
putchar(x%10+48);
}
#define N 10005
int n,k;
string st,tt;
string in1[N],in2[N];
struct Type
{
string Name;
vector< pair<Type*,string> > Member;
int Memory_Size;
int Memory_Align;
Type()
{
Name.clear();
Member.clear();
Memory_Align=Memory_Size=0;
}
Type(string s,int i):Name(s),Memory_Size(i),Memory_Align(i){}
};
vector<Type> type;
map<string,Type*> NtT;
struct Element
{
Type Element_Type;
string Name;
int Element_Address;
Element()
{
Name.clear();
Element_Address=0;
}
Element(Type _type,string _name,int _addr):
Element_Type(_type),Name(_name),Element_Address(_addr){}
};
vector<Element> element;
map<string,Element*> NtE;
int addr_pos;
map< pair<int,int>,Element*> AtE;
void init()
{
type.push_back(Type("byte",1));
type.push_back(Type("short",2));
type.push_back(Type("int",4));
type.push_back(Type("long",8));
NtT["byte"]=&type[0];
NtT["short"]=&type[1];
NtT["int"]=&type[2];
NtT["long"]=&type[3];
}
void op1(string name,int num,string* s,string* t)
{
Type new_type;
new_type.Name=name;
for(int i=1;i<=num;i++)
new_type.Member.push_back(pair(NtT[s[i]],t[i]));
int cnt=0;
for(auto it:new_type.Member)
{
Type tmp=*it.first;
new_type.Memory_Align=max(new_type.Memory_Align,tmp.Memory_Align);
if(cnt%tmp.Memory_Align)
cnt=(cnt/tmp.Memory_Align+1)*tmp.Memory_Align;
cnt+=tmp.Memory_Size;
}
if(cnt%new_type.Memory_Align)
cnt=(cnt/new_type.Memory_Align+1)*new_type.Memory_Align;
new_type.Memory_Size=cnt;
type.push_back(new_type);
NtT[name]=&type.back();
}
void op2(string name,string ele)
{
Element new_element;
int _align=NtT[name]->Memory_Align;
int _size=NtT[name]->Memory_Size;
if(addr_pos%_align)
addr_pos=(addr_pos/_align+1)*_align;
new_element=Element(*NtT[name],ele,addr_pos);
element.push_back(new_element);
NtE[ele]=&element.back();
AtE[pair(addr_pos,addr_pos+_size-1)]=&element.back();
addr_pos+=_size;
}
void op3(string ele)
{
list<string> name;
string tmp;
tmp.clear();
for(auto i:ele)
{
if(ele[i]=='.')
name.push_back(tmp),tmp.clear();
else tmp+=ele[i];
}
name.push_back(tmp);
int _pos=NtE[name.front()]->Element_Address;
Type _type=NtE[name.front()]->Element_Type;
name.pop_front();
while(!name.empty())
{
string _name=name.front();
name.pop_front();
for(auto it:_type.Member)
{
if(_pos& it.first->Memory_Align)
_pos=(_pos/it.first->Memory_Align+1)*it.first->Memory_Align;
if(it.second==_name)
{
_type= *it.first;
break;
}
else _pos+=it.first->Memory_Size;
}
}
write(_pos);
puts("");
}
void op4(int _addr)
{
if(_addr>=addr_pos)
{
cout<<"ERR"<<'\n';
rt;
}
Element ele;
for(auto it:AtE)
if(_addr>=it.first.first && _addr<=it.first.second)
{
ele=*it.second;
break;
}
int _pos=_addr-ele.Element_Address;
int _begin=0,_end;
Type _type=ele.Element_Type;
string _name;
_name.clear();
_name+=ele.Name;
while(!_type.Member.empty())
{
for(vector< pair<Type*,string> >::iterator it=_type.Member.begin();
it!=_type.Member.end();it++)
{
_end=_begin+(*it).first->Memory_Size;
if(_pos<_end && _pos>=_begin)
{
_name+='.'+(*it).second;
_type=*(*it).first;
break;
}
else
{
if(it+1==_type.Member.end())
{
cout<<"ERR"<<'\n';
rt;
}
int _align=(*(it+1)).first->Memory_Align;
if(_end%_align)
_end=(_end/_align+1)*_align;
if(_pos<_end)
{
cout<<"ERR"<<'\n';
rt;
}
_begin=_end;
}
}
}
if(_name==" ")
{
cout<<"ERR"<<'\n';
rt;
}
cout<<_name<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
n=read();
init();
while(n--)
{
int op=read();
switch(op)
{
case 1:
cin>>st;
k=read();
for(int i=1;i<=k;i++)
cin>>in1[i]>>in2[i];
op1(st,k,in1,in2);
write(type.back().Memory_Size);
putchar(' ');
write(type.back().Memory_Align);
puts("");
break;
case 2:
cin>>st>>tt;
op2(st,tt);
write(element.back().Element_Address);
puts("");
break;
case 3:
cin>>st;
op3(st);
break;
case 4:
cin>>k;
op4(k);
break;
}
}
}