編譯器的概述

首先特別感謝網易公司,網易公司提供的雲課堂,公開課實在是太好了,有了這些東西我們這些渣渣學校的也可以學習國內外名校的課程了。

由於我在大學學習的網絡工程,大學的時候自己也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;
}




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