编译器的概述

首先特别感谢网易公司,网易公司提供的云课堂,公开课实在是太好了,有了这些东西我们这些渣渣学校的也可以学习国内外名校的课程了。

由于我在大学学习的网络工程,大学的时候自己也too young,学校没教《编译原理》这门课,自己也没主动接触过,看到许多大牛都很推荐学习《编译原理》这门课堂(程序员的三大浪漫之一:操作系统,编译原理,图形学),所以我也就很关注网易云课堂的这门课,最近这门课终于开了,所以把自己的学习历程记录下来吧,希望课程结束的时候自己能够写出来一个简单的编译器。

课程主页在这里:网易云课堂--中国科学技术大学--编译原理

注:有些图片直接从该课程讲师 @华保健 老师提供的ppt中截取。已经在征取老师的意见了,如果不合适的话,我会重新制作插图。


什么是编译器

编译器是一个程序,它的核心功能是阅读以某种语言(源语言如C、C++)编写的程序,然后把该程序翻译为和它等价的,用目标语言(如二进制机器码)编写的程序。编译器重要的任务之一是报告翻译过程中发现的源程序中的错误。简单来说编译器就是完成源程序到目标程序的转换。


编译器和解释器

编译器和解释器都是处理程序的程序。编译器把源程序翻译为目标程序,而解释器直接执行源程序并计算结果。在把用户输入映射为输出的过程中,由编译器产生的机器语言目标程序通常比解释器快很多,但是解释器的错误诊断比编译器好,因为解释器是逐语句执行源程序。

编译器的结构

实际开发中,我们可以认为编译器就是一个黑盒子,它可以接受源程序翻译为目标程序,打开这个黑盒子,可以认为编译器由两部分组成:

1:分析部分

分析部分也称为编译器的前端,它处理源语言相关的东西。可以粗略认为前端由词法分析,语法分析,语义分析这几个部分组成。
词法分析的输入是一个字符流(可以认为是存储在硬盘上的源代码文件),输出是一组有意义的单词(关键字,变量名,操作符都是一个单词)和符号表。
语法分析处理词法分析的输出单词,然后输出一个语法树,这个语法树表示程序的逻辑结构。
语义分析使用语法树和符号表进行类型检查,类型转换等操作判断是否满足该语言的(编码)规则。

经过前端处理之后,得到源程序的中间表示。

2:综合部分

综合部分也称为编译器的后端,它使用源程序的中间表示和符号表来构造目标程序。通常在前端和后端之间还会有一些与机器无关的优化操作。优化是在中间表示上进行转换,以便后端程序能够更好的生成目标程序。


编译器的优化

编译器的优化指的是生成的目标代码尽可能的高效。编译器的优化必须满足下面的设计目标:
  • 优化必须是正确的。也就是说,不能改变被编译程序的意思
  • 优化必须能够改善很多程序的性能
  • 优化所需要的时间必须在可接受范围内
  • 优化所需要的工程方面的工作必须是可管理的
对正确性的强调是无论如何都不过分的,不管编译得到的目标程序运行有多块,只要生成的代码不正确,再快也没有意义。因此设计编译器的最重要的目标就是正确性。性能意味着执行效率和能耗,嵌入式设备和移动设备对性能的要求更高。当然,优化工作需要的时间必须可接受,如果优化需要几天的时间,就算优化后程序执行的再快,也没有人能接受。

编译器的第一个课堂作业

这个课堂作业同样取自华老师的课件,华老师给出了c语言的部分实现,我这里用C++重写。
题目如下:现在有一个叫做sum的语言,目标平台是栈式计算机,支持两条指令PUSH和ADD,模拟2+3+4的编译过程(这里不牵扯后端)。
#include <iostream>
#include <vector>
#include <cassert>

class Expression
{
public:
	Expression() { }
	virtual ~Expression() { }

	enum EXP_TYPE { ET_INT, ET_SUM };
	virtual EXP_TYPE type() = 0;
};

class Expression_Int : public Expression
{
public:
	Expression_Int(int v) : value_(v) { }
	virtual ~Expression_Int() { }

	virtual Expression::EXP_TYPE type() override
	{ return Expression::ET_INT; }

	int value() const { return value_; }

private:
	int value_;
};

class Expression_Sum : public Expression
{
public:
	Expression_Sum(Expression* left, Expression* right)
		: left_(left), right_(right) { }
	virtual ~Expression_Sum() { }

	virtual Expression::EXP_TYPE type() override
	{ return Expression::ET_SUM; }

	Expression* left() { return left_; }
	Expression* right() { return right_; }

private:
	Expression* left_;
	Expression* right_;
};

class Compiler
{
	struct StackElem
	{
		enum ELEM_TYPE { ET_PUSH, ET_ADD };
		
		ELEM_TYPE type_;
		union
		{
			int value;
		} context;
	};

public:
	void process(Expression* exp)
	{
		if (!exp) return;
		switch (exp->type())
		{
		case Expression::ET_INT:
		{
			Expression_Int* p = dynamic_cast<Expression_Int*>(exp);
			assert(p);
			StackElem se;
			se.type_ = StackElem::ET_PUSH;
			se.context.value = p->value();
			elemVec.push_back(se);
			break;
		}
		case Expression::ET_SUM:
		{
			Expression_Sum* p = dynamic_cast<Expression_Sum*>(exp);
			assert(p);
			process(p->left());
			process(p->right());
			StackElem se;
			se.type_ = StackElem::ET_ADD;
			elemVec.push_back(se);
			break;
		}
		default:
			break;
		}
	}

	void show()
	{
		for (auto e : elemVec)
		{
			switch (e.type_)
			{
			case StackElem::ET_ADD:
			{
				std::cout << "ADD" << std::endl;
				break;
			}
			case StackElem::ET_PUSH:
			{
				std::cout << "PUSH " << e.context.value << std::endl;
				break;
			}
			default:
				break;
			}
		}
	}

private:
	std::vector<StackElem> elemVec; //stack
};

int main()
{
	std::cout << "Compile starting" << std::endl;
	// build an expression tree:
	//            +
	//           / \
	//          +   4
	//         / \
	//        2   3

	Expression* left = new Expression_Sum(new Expression_Int(2), new Expression_Int(3));
	Expression* right = new Expression_Int(4);
	Expression* exp = new Expression_Sum(left, right);

	Compiler c;
	c.process(exp);
	c.show();

	return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章