语法分析是最难写的,而这部分确实最伤脑的。大量的语义动作分析差点把我逼疯。

  简而言之,这部分的作用就是在每次归约之后,都进行一些语义动作,最终让我们得到测试程序的三地址码,即中间代码。

1. 新的数据结构和函数

为了得到中间代码,我引进了几个struct,如下:

//用于标识一个变量的类型
enum TYPE
{
INT,
BOOL,
ARRAY
}; //状态栈中最重要的信息
struct Info {
std::string name = ""; //变量名或者运算符
int quad;
int val;
int width; //不同类型的所占偏移
TYPE t;
std::unordered_set<int> truelist, falselist, nextlist;
int array_offset = 0;
std::vector<std::string> code;
std::string other = ""; //辅助
}; //每次归约时用到的状态栈
struct stack_node {
int status;
Word token;
Info nodeInfo;
}; //符号表表项
struct Symbol {
TYPE t;
int n = 0 ;
int offset; //该作用域下的偏移
};

  当然还定义了新的类进行状态栈分析,如下:

class Parser {
public:
typedef std::unordered_map<int, std::unordered_map<std::string, std::string>> Atable;
typedef std::unordered_map<int, std::unordered_map<std::string, int>> Gtable;
typedef std::unordered_map<std::string, Symbol> STable; Parser();
void getTable(std::ifstream &i1, std::ifstream &i2);
void get_grammer(std::ifstream &in);
bool stack_parser(std::ifstream &in); //主要分析过程
void print(std::ofstream &out);
void run();
Symbol hasKey(std::string str);
void gen_code(std::string str); //生成三地址码
void gen_math_code(std::vector<stack_node> p);
void backpatch(std::unordered_set<int> l, int addr); //回填
std::unordered_set<int> merge(std::unordered_set<int> s1, std::unordered_set<int> s2); private:
std::stack<stack_node> stk;
std::vector<int> parser_result, parser_line;
Atable action;
Gtable goTo;
std::vector<Grammer> G;
std::vector<std::string> code; //三地址码
std::stack<STable *> Utable; //符号表
int offset_record[MAX_LAY] = {0};
int tmp_count; //临时变量的下标
};

  有了这些东西,我们就可以进行语义动作设计了。

2. 语义动作设计

  直接上代码,每个状态都有各自的语义动作,我是用switch-case写的,如下:

#include "myParser.h"

Parser::Parser() {
stack_node pNode;
pNode.status = 0;
tmp_count = 0;
pNode.token.setTag(70);
pNode.token.setLexeme("#"); stk.push(pNode);
run();
} void Parser::getTable(std::ifstream &i1, std::ifstream &i2) {
std::string tmp2,atmp3;
int tmp1,gtmp3;
while(i1 >> tmp1, i1 >> tmp2, i1 >> atmp3) {
action[tmp1][tmp2] = atmp3;
} while(i2 >> tmp1, i2 >> tmp2, i2 >> gtmp3) {
goTo[tmp1][tmp2] = gtmp3;
} } void Parser::get_grammer(std::ifstream &in) {
std::string tmp;
Grammer gtmp;
gtmp.left = "S";
gtmp.right = {"Program"};
G.push_back(gtmp); while(in >> tmp) {
gtmp.left = tmp;
gtmp.right.clear();
in >> tmp >> tmp;
while(tmp != "#") {
gtmp.right.push_back(tmp);
in >> tmp;
}
G.push_back(gtmp);
}
} inline
void Parser::gen_code(std::string str) {
code.push_back(str);
} void Parser::gen_math_code(std::vector<stack_node> p) {
std::string left, right, op; left = p[2].nodeInfo.name;
op = p[1].nodeInfo.name;
right = p[0].nodeInfo.name; code.push_back("t"+std::to_string(tmp_count++)+" = "+left + " "+op+" "+right); } void Parser::backpatch(std::unordered_set<int> l, int addr) {
for(auto t : l)
code[t] = code[t] + std::to_string(addr);
} std::unordered_set<int> Parser::merge(std::unordered_set<int> s1, std::unordered_set<int> s2) {
std::unordered_set<int> result = s1;
result.insert(s2.begin(),s2.end());
return result;
} Symbol Parser::hasKey(std::string str) {
Symbol result ;
std::stack<STable *> stack_tmp = Utable;
STable st_tmp; while(!stack_tmp.empty()) {
st_tmp = *stack_tmp.top();
if(st_tmp.find(str) != st_tmp.end())
return st_tmp[str];
stack_tmp.pop();
} result.n = ERROR;
return result;
} bool Parser::stack_parser(std::ifstream &in) { in >> std::noskipws;
Lexer mylex; Word w;
w = mylex.scan(in); STable *stable;
Symbol sym_tmp;
stack_node stk_top; std::string left = "", right = ""; while(!stk.empty()) {
stk_top = stk.top();
int top_status = stk_top.status;
int next_status;
std::string act; //将要执行的动作
std::string T;
if(w.getTag() == 50)
T = "Num";
else if(w.getTag() == 60)
T = "Identifier";
else
T = w.getLexeme(); if(action[top_status].find(T) == action[top_status].end())
return false;
act = action[top_status][T];
stack_node tmp;
if(act[0] == 's') {
//规定作用域
if(w.getLexeme() == "{") {
offset_record[Utable.size()] = 0;
stable = new STable();
Utable.push(stable);
}
else if(w.getLexeme() == "}") {
Utable.pop();
if(!Utable.empty())
stable = Utable.top();
}
//移进动作
next_status = std::stoi(act.substr(1));
tmp.status = next_status;
tmp.token = w;
tmp.nodeInfo.name = w.getLexeme();
if(w.getTag() == 50)
tmp.nodeInfo.val = std::stoi(w.getLexeme());
stk.push(tmp);
w = mylex.scan(in);
} else if(act[0] == 'r') {
std::vector<stack_node> right_tmp;
next_status = std::stoi(act.substr(1));
if(G[next_status].right[0] != "ε") {
for(int i = 0; i < static_cast<int>(G[next_status].right.size());i++) {
right_tmp.push_back(stk.top());
stk.pop();
}
}
stk_top = stk.top();
top_status = stk_top.status;
if(goTo[top_status].find(G[next_status].left) == goTo[top_status].end())
return false;
tmp.status = goTo[top_status][G[next_status].left];
tmp.token.setLexeme(G[next_status].left);
tmp.token.setTag(next_status); //std::cout << next_status << " "; switch(next_status) {
case 0:
break;
case 1:
break;
case 2:
tmp.nodeInfo.t = INT;
tmp.nodeInfo.width = 4;
break;
case 3:
tmp.nodeInfo.t = BOOL;
tmp.nodeInfo.width = 1;
break;
case 4:
backpatch(right_tmp[4].nodeInfo.nextlist, code.size());
break;
case 5:
tmp.nodeInfo = right_tmp[1].nodeInfo;
sym_tmp = hasKey(tmp.nodeInfo.name);
if(stable->find(tmp.nodeInfo.name) == stable->end()) {
//创建符号表并且初始化为0
Symbol s_tmp;
int size_tmp = 1;
if(tmp.nodeInfo.array_offset > 0) {
size_tmp *= tmp.nodeInfo.array_offset;
//向符号表里添加数据
size_tmp *= right_tmp[2].nodeInfo.width;
s_tmp.t = ARRAY;
s_tmp.offset = offset_record[Utable.size()-1];
s_tmp.n = tmp.nodeInfo.array_offset;
offset_record[Utable.size()-1] += size_tmp; stable->insert({tmp.nodeInfo.name,s_tmp});
} else {
size_tmp = right_tmp[2].nodeInfo.width;
//向符号表添加数据
s_tmp.t = tmp.nodeInfo.t;
s_tmp.offset = offset_record[Utable.size()-1];
offset_record[Utable.size()-1] += size_tmp; stable->insert({tmp.nodeInfo.name,s_tmp});
}
} else
std::cout <<"hh" ;
;//重复声明,error; break;
case 6:
backpatch(right_tmp[2].nodeInfo.nextlist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.nextlist = right_tmp[0].nodeInfo.nextlist;
break;
case 7:
tmp.nodeInfo.nextlist = right_tmp[0].nodeInfo.nextlist;
break;
case 8:
tmp.nodeInfo = right_tmp[3].nodeInfo;
//符号表中不存在,则创建
sym_tmp = hasKey(tmp.nodeInfo.name);
if(stable->find(tmp.nodeInfo.name) == stable->end() && stk_top.token.getLexeme() == "Type")
tmp.nodeInfo.array_offset = right_tmp[1].nodeInfo.val;
else {
if(sym_tmp.t == ARRAY) {
std::string code_tmp;
code_tmp = "t" + std::to_string(tmp_count++) + " = " + std::to_string(sym_tmp.offset);
gen_code(code_tmp);
code_tmp= "t" + std::to_string(tmp_count++) + " = ";
code_tmp = code_tmp + "t"+std::to_string(tmp_count-2)+"["+ std::to_string(right_tmp[1].nodeInfo.val)+"]";
gen_code(code_tmp);
tmp.nodeInfo.name = "t"+std::to_string(tmp_count - 1);
}
else
;//普通变量引用数组
}
break;
case 9:
tmp.nodeInfo = right_tmp[3].nodeInfo;
sym_tmp = hasKey(tmp.nodeInfo.name);
if(sym_tmp.n >= 0) {
if(sym_tmp.t == ARRAY){
Symbol sym_tmpp = hasKey(right_tmp[1].nodeInfo.name);
if(sym_tmpp.n >= 0) {
std::string code_tmp;
code_tmp = "t" + std::to_string(tmp_count++) + " = " + std::to_string(sym_tmp.offset);
gen_code(code_tmp);
code_tmp = "t" + std::to_string(tmp_count++) + " = ";
code_tmp = code_tmp + "t"+std::to_string(tmp_count-2)+"["+right_tmp[1].nodeInfo.name+"]";
gen_code(code_tmp);
tmp.nodeInfo.name = "t"+std::to_string(tmp_count-1);
}
else
;//error
}
else
;//普通变量引用数组
} else
;//声明数组用变量
break;
case 10:
tmp.nodeInfo.name = right_tmp[0].nodeInfo.name;
break;
case 11:
tmp.nodeInfo.name = "1";
tmp.nodeInfo.truelist.insert(code.size());
break;
case 12:
tmp.nodeInfo.name = "0";
tmp.nodeInfo.falselist.insert(code.size());
break;
case 13:
//计算左右边的值
if(right_tmp[2].nodeInfo.other == "")
left = right_tmp[2].nodeInfo.name;
else
left = right_tmp[2].nodeInfo.name +"["+right_tmp[2].nodeInfo.other+"]"; right = right_tmp[0].nodeInfo.name; gen_code(left+" = "+right); break;
case 14:
tmp.nodeInfo = right_tmp[1].nodeInfo;
if(tmp.nodeInfo.other != "") {
std::string code_tmp;
code_tmp="t"+std::to_string(tmp_count++)+" = ";
left = tmp.nodeInfo.name+"["+tmp.nodeInfo.other+"]";
code_tmp += left;
gen_code(code_tmp);
right = "t"+std::to_string(tmp_count-1);
code_tmp = right +" = "+right+" "+right_tmp[0].nodeInfo.name + " 1";
gen_code(code_tmp);
gen_code(left+" = "+ right);
} else
gen_code(tmp.nodeInfo.name + " = "+tmp.nodeInfo.name+" "+right_tmp[0].nodeInfo.name+" 1");
break;
case 15:
tmp.nodeInfo = right_tmp[0].nodeInfo;
if(tmp.nodeInfo.other != "") {
std::string code_tmp;
code_tmp="t"+std::to_string(tmp_count++)+" = ";
left = tmp.nodeInfo.name+"["+tmp.nodeInfo.other+"]";
code_tmp += left;
gen_code(code_tmp);
right = "t"+std::to_string(tmp_count-1);
code_tmp = right +" = "+right+" "+right_tmp[1].nodeInfo.name + " 1";
gen_code(code_tmp);
gen_code(left+" = "+ right);
} else
gen_code(tmp.nodeInfo.name + " = "+tmp.nodeInfo.name+" "+right_tmp[1].nodeInfo.name+" 1");
break;
case 16:
backpatch(right_tmp[3].nodeInfo.falselist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.falselist = right_tmp[0].nodeInfo.falselist;
tmp.nodeInfo.truelist = merge(right_tmp[0].nodeInfo.truelist,right_tmp[3].nodeInfo.truelist);
break;
case 17:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 18:
backpatch(right_tmp[3].nodeInfo.truelist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.truelist = right_tmp[0].nodeInfo.truelist;
tmp.nodeInfo.falselist = merge(right_tmp[0].nodeInfo.falselist,right_tmp[3].nodeInfo.falselist);
break;
case 19:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 20:
tmp.nodeInfo.truelist.insert(code.size());
tmp.nodeInfo.falselist.insert(code.size()+1);
gen_code("if "+right_tmp[2].nodeInfo.name+" "+right_tmp[1].nodeInfo.name+" "+right_tmp[0].nodeInfo.name+" goto ");
gen_code("goto ");
break;
case 21:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 22:
gen_math_code(right_tmp);
tmp.nodeInfo.name = "t"+std::to_string(tmp_count-1);
break;
case 23:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 24:
gen_math_code(right_tmp);
tmp.nodeInfo.name = "t"+std::to_string(tmp_count-1);
break;
case 25:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 26:
tmp.nodeInfo.truelist = right_tmp[1].nodeInfo.falselist;
tmp.nodeInfo.falselist = right_tmp[1].nodeInfo.truelist;
break;
case 27:
tmp.nodeInfo = right_tmp[1].nodeInfo;
break;
case 28:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 29:
tmp.nodeInfo = right_tmp[0].nodeInfo;
tmp.nodeInfo.name = right_tmp[0].token.getLexeme();
break;
case 30:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 31:
tmp.nodeInfo.name = "+";
break;
case 32:
tmp.nodeInfo.name = "-";
break;
case 33:
break;
case 34:
break;
case 35:
tmp.nodeInfo.name = "+";
break;
case 36:
tmp.nodeInfo.name = "-";
break;
case 37:
tmp.nodeInfo.name = "*";
break;
case 38:
tmp.nodeInfo.name = "/";
break;
case 39:
tmp.nodeInfo.name = "%";
break;
case 40:
tmp.nodeInfo.name = "==";
break;
case 41:
tmp.nodeInfo.name = "!=";
break;
case 42:
tmp.nodeInfo.name = ">=";
break;
case 43:
tmp.nodeInfo.name = "<=";
break;
case 44:
tmp.nodeInfo.name = ">";
break;;
case 45:
tmp.nodeInfo.name = "<";
break;
case 46:
break;
case 47:
backpatch(right_tmp[3].nodeInfo.truelist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.nextlist = merge(right_tmp[3].nodeInfo.falselist,right_tmp[0].nodeInfo.nextlist);
break;
case 48:
backpatch(right_tmp[7].nodeInfo.truelist,right_tmp[5].nodeInfo.quad);
backpatch(right_tmp[7].nodeInfo.falselist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.nextlist = merge(right_tmp[4].nodeInfo.nextlist,merge(right_tmp[3].nodeInfo.nextlist,right_tmp[0].nodeInfo.nextlist));
break;
case 49:
break;
case 50:
backpatch(right_tmp[0].nodeInfo.nextlist,right_tmp[5].nodeInfo.quad);
backpatch(right_tmp[3].nodeInfo.truelist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.nextlist = right_tmp[3].nodeInfo.falselist;
gen_code("goto "+std::to_string(right_tmp[5].nodeInfo.quad));
break;
case 51:
backpatch(right_tmp[0].nodeInfo.nextlist,right_tmp[6].nodeInfo.quad);
backpatch(right_tmp[5].nodeInfo.truelist,right_tmp[1].nodeInfo.quad);
tmp.nodeInfo.nextlist = right_tmp[5].nodeInfo.falselist;
for(auto cd : right_tmp[3].nodeInfo.code)
gen_code(cd);
gen_code("goto "+std::to_string(right_tmp[6].nodeInfo.quad));
break;
case 52:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 53:
break;
case 54:
tmp.nodeInfo = right_tmp[0].nodeInfo;
break;
case 55:
break;
case 56:
tmp.nodeInfo = right_tmp[1].nodeInfo;
break;
case 57:
tmp.nodeInfo = right_tmp[3].nodeInfo;
//符号表中不存在,则创建
sym_tmp = hasKey(tmp.nodeInfo.name); if(sym_tmp.t == ARRAY) {
std::string code_tmp;
code_tmp = "t" + std::to_string(tmp_count++) + " = " + std::to_string(sym_tmp.offset);
gen_code(code_tmp);
tmp.nodeInfo.other = std::to_string(right_tmp[1].nodeInfo.val);
tmp.nodeInfo.name = "t"+std::to_string(tmp_count-1);
}
else
;//普通变量引用数组
break;
case 58:
tmp.nodeInfo = right_tmp[3].nodeInfo;
sym_tmp = hasKey(tmp.nodeInfo.name);
if(sym_tmp.t == ARRAY){
Symbol sym_tmpp = hasKey(right_tmp[1].nodeInfo.name);
if(sym_tmpp.n >= 0) {
std::string code_tmp;
code_tmp = "t" + std::to_string(tmp_count++) + " = " + std::to_string(sym_tmp.offset);
gen_code(code_tmp);
tmp.nodeInfo.other = right_tmp[1].nodeInfo.name;
tmp.nodeInfo.name = "t"+std::to_string(tmp_count-1);
}
else
;//error
} else
;//声明数组用变量
break;
case 59:
tmp.nodeInfo.name = right_tmp[0].nodeInfo.name;
break;
case 60:
tmp.nodeInfo.quad = code.size();
break;
case 61:
tmp.nodeInfo.nextlist.insert(code.size());
gen_code("goto ");
break;
case 62:
//计算左右边的值
if(right_tmp[2].nodeInfo.other == "")
left = right_tmp[2].nodeInfo.name;
else
left = right_tmp[2].nodeInfo.name +"["+right_tmp[2].nodeInfo.other+"]"; right = right_tmp[0].nodeInfo.name; tmp.nodeInfo.code.push_back(left+" = "+right); break;
case 63:
tmp.nodeInfo = right_tmp[1].nodeInfo;
if(tmp.nodeInfo.other != "") {
std::string code_tmp;
code_tmp="t"+std::to_string(tmp_count++)+" = ";
left = tmp.nodeInfo.name+"["+tmp.nodeInfo.other+"]";
code_tmp += left;
tmp.nodeInfo.code.push_back(code_tmp);
right = "t"+std::to_string(tmp_count-1);
code_tmp = right +" = "+right+" "+right_tmp[0].nodeInfo.name + " 1";
tmp.nodeInfo.code.push_back(code_tmp);
tmp.nodeInfo.code.push_back(left+" = "+right);
} else
tmp.nodeInfo.code.push_back(tmp.nodeInfo.name + " = "+tmp.nodeInfo.name+" "+right_tmp[0].nodeInfo.name+" 1");
break;
case 64:
tmp.nodeInfo = right_tmp[0].nodeInfo;
if(tmp.nodeInfo.other != "") {
std::string code_tmp;
code_tmp="t"+std::to_string(tmp_count++)+" = ";
left = tmp.nodeInfo.name+"["+tmp.nodeInfo.other+"]";
code_tmp += left;
tmp.nodeInfo.code.push_back(code_tmp);
right = "t"+std::to_string(tmp_count-1);
code_tmp = right +" = "+right+" "+right_tmp[1].nodeInfo.name + " 1";
tmp.nodeInfo.code.push_back(code_tmp);
tmp.nodeInfo.code.push_back(left+" = "+right);
} else
tmp.nodeInfo.code.push_back(tmp.nodeInfo.name + " = "+tmp.nodeInfo.name+" "+right_tmp[1].nodeInfo.name+" 1");
break;
case 65:
break;
case 66:
break;
default:
;
}
stk.push(tmp);
} else
return true;
if(in.eof()) {
w.setLexeme("#");
w.setTag(0);
}
}
return false;
} void Parser::print(std::ofstream &out) {
for(int i = 0; i < static_cast<int>(code.size());i++) {
out << i << " : " << code[i] << std::endl;
}
} void Parser::run() { std::ifstream gin("grammer.txt");
std::ifstream lin("/root/C++/Compiler/test.txt");
std::ifstream action_in("action.out");
std::ifstream goto_in("goto.out");
std::ofstream parser_out("/root/C++/Compiler/parser.out"); getTable(action_in,goto_in);
get_grammer(gin);
if(stack_parser(lin))
print(parser_out);
gin.close();
lin.close();
action_in.close();
goto_in.close(); }

简单的C语言编译器--语义制导翻译的更多相关文章

  1. 简单的C语言编译器--概述

      在学习了编译原理的相关知识后,逐渐的掌握一个编译器的结构.作用和实现方法.同时,希望自己在不断的努力下写出一个简单的C语言编译器. 实现步骤 词法分析器:将C语言测试代码分解成一个一个的词法单元: ...

  2. 简单的C语言编译器--语法分析器

      语法分析算是最难的一部分了.总而言之,语法分析就是先设计一系列语法,然后再用设计好的语法去归约词法分析中的结果.最后将归约过程打印出来,或者生成抽象语法树. 1. 设计文法 以下是我的文法(引入的 ...

  3. 简单的C语言编译器--词法分析器

    1. 定义词法单元Tag   首先要将可能出现的词进行分类,可以有不同的分类方式.如多符一类:将所有逗号.分号.括号等都归为一类,或者一符一类,将一个符号归为一类.我这里采用的是一符一类的方式.C代码 ...

  4. 第一个C语言编译器是怎样编写的?

    首先向C语言之父Dennis MacAlistair Ritchie致敬! 当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用C语言编写的,有一些语言比如Clojure,Jython等是基于J ...

  5. Go语言安全编码规范-翻译(分享转发)

    Go语言安全编码规范-翻译 本文翻译原文由:blood_zer0.Lingfighting完成 如果翻译的有问题:联系我(Lzero2012).匆忙翻译肯定会有很多错误,欢迎大家一起讨论Go语言安全能 ...

  6. C语言编译器开发之旅(开篇)

    编译器写作之旅   最近在Github上看到一个十分有趣的项目acwj(A Compiler Writing Journey),一个用C语言编写编译器的项目.身为一个程序员,这在我看来是一件十分酷的事 ...

  7. 【转】自己动手写SC语言编译器

    自序 编译原理与技术的一整套理论在整个计算机科学领域占有相当重要的地位,学习它对程序设计人员有很大的帮助.我们考究历史会发现那些人人称颂的程序设 计大师都是编译领域的高手,像写出BASIC语言的BIL ...

  8. 在线C语言编译器/解释器

    在线C语言编译器/解释器 本文介绍两个C语言在线解释器/编译器,这些工具可以提高代码片段检测方便的工作效率,并可以保证这些代码的正确性,而且还可以和别人一起编辑/分享之间的代码,这样可以共同分析代码并 ...

  9. 一个简单的C语言程序(详解)

    C Primer Plus之一个简单的C语言程序(详解) #include <stdio.h> int main(void) //一个简单的 C程序 { int num; //定义一个名为 ...

随机推荐

  1. Axure 入门学习

    Axure RP是美国Axure Software Solution公司旗舰产品,是一个专业的快速原型设计工具,让负责定义需求和规格.设计功能和界面的专家能够快速创建应用软件或Web网站的线框图.流程 ...

  2. linux三剑客之一grep

    正则表达式(Regular Expression) 目录 起源 正则表达式是什么? 两类基本正则表达式 正则表达式元字符 Linux上文本三剑客 小试牛刀 总结 一.起源   正则表达式这个名词,相信 ...

  3. [BZOJ1606] [Usaco2008 Dec] Hay For Sale 购买干草 (dp)

    Description 约翰遭受了重大的损失:蟑螂吃掉了他所有的干草,留下一群饥饿的牛.他乘着容量为C(1≤C≤50000)个单位的马车,去顿因家买一些干草.  顿因有H(1≤H≤5000)包干草,每 ...

  4. [BZOJ1610] [Usaco2008 Feb] Line连线游戏 (set)

    Description Farmer John最近发明了一个游戏,来考验自命不凡的贝茜.游戏开始的时 候,FJ会给贝茜一块画着N (2 <= N <= 200)个不重合的点的木板,其中第i ...

  5. [BZOJ1503] [NOI2004] 郁闷的出纳员 (treap)

    Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常 ...

  6. 重磅消息-Service Fabric 正式开源

    微软的Azure Service Fabric的官方博客在2017.3.24日发布了一篇博客 Service Fabric .NET SDK goes open source ,介绍了社区呼声最高的S ...

  7. 如何使用mysqldump备份数据库

    一.背景 在开发项目中,数据库是核心资产.除了做主备冗余增加可靠性外,定期备份数据也是必须的. 使用mysqldump备份数据具有操作简单,备份和恢复时间短的优点(mysqldump备份数据生成的是批 ...

  8. 使用Ant打包Java后台程序

    概述 本文通过一个简单的Java Application例子描述如何用ANT完成基本的程序打包工作.包含文件拷贝.编译.打包三部分:完成这三部就可以得到一个可运行的程序包了. ANT的安装,环境变量的 ...

  9. Apache Shiro 标签方式授权

    Shiro提供了一套JSP标签库来实现页面级的授权控制. 在使用Shiro标签库前,首先需要在JSP引入shiro标签: <%@ taglib prefix="shiro"  ...

  10. java抽象类注意问题

    当知道一个类的子类将不同的实现某个方法时,把该类声明为抽象类很有用,可以共用相同的父类方法,不必再定义. 抽象类和抽象方法的关系:含有抽象方法的类一定是抽象类,抽象类里不一定含有抽象方法. 抽象类存在 ...