[P4911]河童重工的计算机 - 题解

原题链接

题意简述

一道大模拟题,要求实现一种汇编语言的编译器。

主要操作有:

  • 内存操作
  • 寄存器操作
  • 函数调用
  • 各种逻辑和数值计算
  • I/O 操作

实现技巧

对于每一行的输入(除去注释),我们开一个结构体存函数名和各个参数。函数名可以使用字符串,参数使用一个多个字符串的数组(具体参见代码)。

可以预处理出一个 map 来存储每一个函数需要的参数个数。注意到题目并没有说一个函数不能输入大于所需个数的参数,但是多余的要过滤掉。

对于内存,开一个静态数组模拟就可以了。

对于 sADDr 栈,开一个栈模拟就可以了。

对于题目里的各个函数,把它们实现成多个独立的函数会更方便。

另外,由于对于一个参数我们需要进行复杂的判断,而这样的判断有很多,所以把取值和赋值分别写成两个函数调用会更好。

取值函数要输入当前行号和参数标号,然后经过以下判断:

  1. 该参数是否为寄存器
  2. 该参数是否为内存地址
  3. 该参数是否为寄存器所存的位置的内存地址
  4. 该参数是否为常量
  5. 该参数是否为编译时常量

判断完毕后根据参数的含义输出其中的值就行了。

赋值函数要输入当前行号和参数标号,然后经过以下判断:

  1. 该参数是否为寄存器
  2. 该参数是否为内存地址
  3. 该参数是否为寄存器对应的内存地址

判断完后根据参数含义改变其数值即可。

为便于调试和写部分分,建议先从 I/O 操作开始实现代码。

使用匿名函数实现题目所指的各个函数,可以方便地使用 map 来解决函数调用。以作者代码举例,直接使用 fc[fu[i].name](i) 即可访问第 i 行的函数。

要点概括

有如下难点:

  • 函数调用
  • 输入源代码
  • 注释处理

函数调用

开一个数组,存函数开始位置,在输入时如果输入到了函数定义就存一下对应的行号,在主循环上不使用 i++,而是实现一个跳转用的变量,调用的时候直接跳转到行。

输入源代码

需要自己写快读,每个单词开一个字符串,如果当前位是合法字符(不为注释和符号)就拼在字符串后面,输入完遇到空格或者分号就写入结构体然后清空。然后输入参数,与之类似,但是记得在输入个数足够的参数后就停止,不要输入过多个数的参数导致错误和超出数组长度。

换行一定用分号判!!!

经实验,判断换行符极易导致最后一个点 TLE,猜测不只有 '\n' 换行。

注释处理

实现一个变量,记录是否遇到左括号,但是只用实现为 bool 类型,不需要累加个数,因为只有第一个左括号作数,当它碰到第一个右括号就结束了注释。

注释嵌套不用管。

[[][] 等价,两个方括号中间的全是注释,这之间不用管单个左括号,只用看右括号。

指导

对于 string 类型,我们使用 map<string,int> 就可以实现在方括号中直接填寄存器名称字符串就可以访问到寄存器值的效果。

另外,map<string,function<void(int)>> 则可以直接把方括号填函数名的 map 当成函数,比如 fc[fu[i].name](i) 就可以直接访问第 i 行对应的函数,输入的参数为 i,但是这要求所有函数都是同一个类型(如 void),并且参数个数要一致,不过这在本题中很适用。

总结

这是一道很好的大模拟入门题,很适合作为新手第一道大模拟题,需要细心和耐心。

另外,大模拟考验程序设计能力,抄袭易被抓哦。

代码

#include<bits/stdc++.h>
using namespace std;
struct func{string name,c[11];} fu[1000005];//每一行的函数
int d[16777217],qp,ff,opt=1,pl,n;//d模拟内存
map<string,int> b={{"r1",0},{"r2",0},{"r3",0},{"r4",0},{"e1",0},{"e2",0},{"e3",0},{"e4",0},{"flag",0},{"val",0},{"ret",0},{"line",0}},f={{"udef",0},{"hlt",0},{"nop",0},{"set",2},{"jmp",1},{"jif",2},{"call",1},{"ret",1},{"inv",2},{"add",3},{"sub",3},{"mult",3},{"idiv",3},{"mod",3},{"lsft",3},{"rsft",3},{"band",3},{"bor",3},{"bxor",3},{"lgr",3},{"lls",3},{"lge",3},{"lle",3},{"leql",3},{"land",3},{"lor",3},{"rint",1},{"rch",1},{"wint",1},{"wch",1},{"function",1},{"callfunc",1},{"call",1}},fuct;//b存储各个寄存器值,f存储函数需要的参数个数
stack<pair<int,int>> sr;
int main()
{
    scanf("%d",&n);
    auto read=[](int h)//凭个人喜好使用了匿名函数,正常函数也可
    {
        char l=getchar();
        while(l=='\n'||l=='\r') l=getchar();
        string word;
        while(l!='\n'&&l!='\r') 
        {
            if(l==' '||l==';')
            {
                if(word.size()!=0)
                {
                    if(f.count(word)>0&&fu[h].name.size()==0) fu[h].name=word;
                    else
                    {
                        int flag=0;
                        for(int i=1;!flag&&i<=f[fu[h].name];i++) if(fu[h].c[i].size()==0) flag=1,fu[h].c[i]=word;
                    }
                    word.erase();
                }
            }
            else
            {
                if(l=='[') qp=1;
                if(qp==0) if(l!=';') word+=l; 
                if(l==']') qp=0;
            }
            if(l==';') return;
            l=getchar();
        }
    };
    for(int i=1;i<=n;i++) 
    {
        read(i);
        if(fu[i].name=="function")fuct.insert({fu[i].c[1],i});
    }
    auto qz=[&](int h,int w)->int//取值
    {
        if(fu[h].c[w].size()==0) fu[h].c[w]="%val";
        int num,t=0;
        string l;
        if(fu[h].c[w][0]=='%') for(int i=1;i<fu[h].c[w].size();i++) l+=fu[h].c[w][i],t=b[l];
        else if(fu[h].c[w][0]=='@') 
        {
            if(fu[h].c[w][1]=='%') for(int i=2;i<fu[h].c[w].size();i++) l+=fu[h].c[w][i],t=d[b[l]];
            else
            {
                for(int i=1;i<fu[h].c[w].size();i++) t*=10,t+=fu[h].c[w][i]-'0';
                t=d[t];
            }
        }
        else if(fu[h].c[w][0]=='#') 
        {
            if(fu[h].c[w]=="#line") t=h;
            else for(int i=1;i<fu[h].c[w].size();i++) t*=10,t+=fu[h].c[w][i]-'0';
        }
        else for(int i=0;i<fu[h].c[w].size();i++) t*=10,t+=fu[h].c[w][i]-'0';
        return t;
    };
    auto fz=[&](int h,int w,int num)->void//赋值
    {
        if(fu[h].c[w].size()==0) fu[h].c[w]="%val";
        string l;
        if(fu[h].c[w][0]=='%') 
        {
            for(int i=1;i<fu[h].c[w].size();i++) l+=fu[h].c[w][i];
            b[l]=num;
        }
        else if(fu[h].c[w][0]=='@') 
        {
            if(fu[h].c[w][1]=='%') 
            {
                for(int i=2;i<fu[h].c[w].size();i++) l+=fu[h].c[w][i];
                d[b[l]]=num;
            }
            else 
            {
                int t=0;
                for(int i=1;i<fu[h].c[w].size();i++) t*=10,t+=fu[h].c[w][i]-'0';
                d[t]=num;
            }
        }
    };
    auto rint=[&](int h)
    {
        int num;
        scanf("%d",&num);
        fz(h,1,num);
    };
    auto rch=[&](int h){fz(h,1,getchar());};
    auto wint=[&](int h){printf("%d",qz(h,1));};
    auto wch=[&](int h){putchar(qz(h,1));};
    auto add=[&](int h){fz(h,3,qz(h,1)+qz(h,2));};
    auto inv=[&](int h){fz(h,2,-qz(h,1));};
    auto sub=[&](int h){fz(h,3,qz(h,1)-qz(h,2));};
    auto mult=[&](int h){fz(h,3,qz(h,1)*qz(h,2));};
    auto idiv=[&](int h){fz(h,3,qz(h,1)/qz(h,2));};
    auto mod=[&](int h){fz(h,3,qz(h,1)%qz(h,2));};
    auto lsft=[&](int h){fz(h,3,qz(h,1)<<qz(h,2));};
    auto rsft=[&](int h){fz(h,3,qz(h,1)>>qz(h,2));};
    auto band=[&](int h){fz(h,3,qz(h,1)&qz(h,2));};
    auto bor=[&](int h){fz(h,3,qz(h,1)|qz(h,2));};
    auto bxor=[&](int h){fz(h,3,qz(h,1)^qz(h,2));};
    auto lgr=[&](int h){fz(h,3,qz(h,1)>qz(h,2));};
    auto lls=[&](int h){fz(h,3,qz(h,1)<qz(h,2));};
    auto lge=[&](int h){fz(h,3,qz(h,1)>=qz(h,2));};
    auto lle=[&](int h){fz(h,3,qz(h,1)<=qz(h,2));};
    auto leql=[&](int h){fz(h,3,qz(h,1)==qz(h,2));};
    auto land=[&](int h){fz(h,3,qz(h,1)&&qz(h,2));};
    auto lor=[&](int h){fz(h,3,qz(h,1)||qz(h,2));};
    auto call=[&](int h){sr.push({h+1,b["line"]});opt=qz(h,1)-h;};
    auto jmp=[&](int h){opt=b["line"]+qz(h,1)-h;};
    auto ret=[&](int h){if(!sr.empty()){opt=sr.top().first-h;sr.pop();}if(!fu[h].c[1].empty()) b["ret"]=qz(h,1);};
    auto callf=[&](int h){sr.push({h+1,b["line"]});opt=fuct[fu[h].c[1]]-h+1;b["line"]=opt+h-1;};
    auto funct=[&](int h){if(!sr.empty()){opt=h-sr.top().first;}};
    auto set=[&](int h){fz(h,2,qz(h,1));};
    auto jif=[&](int h){if((!fu[h].c[2].empty()&&qz(h,2)!=0)||(fu[h].c[2].empty()&&b["flag"]>0)) opt=qz(h,1)+b["line"]-h;};
    auto stop=[](int h){ff=1;};
    auto udef=[](int h){return;};
    map<string,function<void(int)>> fc={{"rint",rint},{"rch",rch},{"wint",wint},{"wch",wch},{"add",add},{"hlt",stop},{"ret",ret},{"inv",inv},{"sub",sub},{"mult",mult},{"idiv",idiv},{"mod",mod},{"lsft",lsft},{"rsft",rsft},{"band",band},{"bor",bor},{"bxor",bxor},{"function",funct},{"callfunc",callf},{"call",call},{"jmp",jmp},{"set",set},{"jif",jif},{"lgr",lgr},{"lls",lls},{"lge",lge},{"lle",lle},{"leql",leql},{"land",land},{"lor",lor},{"udef",udef},{"nop",udef}};//函数表,凭函数名字符串即可访问到函数
    for(int i=1;!ff&&i<=n;i+=opt,opt=1) fc[fu[i].name](i);
    return 0;
}
posted @ 2025-11-26 17:22  Qaaxaap  阅读(8)  评论(1)    收藏  举报