編譯器-詞法分析

總結

  • 詞法分析就是對字符串進行處理,然後輸出對應的屬性字爲語法分析做準備;
  • 如果對某個單詞進行分析後,不屬於任何屬性字,那麼也要按照錯誤處理
  • 因此詞法分析需要使用到許多字符串處理函數
  • 語法分析就是根據給出的第一個屬性字,然後預測接下來的屬性字,然後讀取下一個屬性字,進行比較,如果和預測相比錯誤,那麼就是語法錯誤

詞法分析

  • 彙編器要做的所有事情並不是在一次同時完成的
  • 語言處理器通常是分爲不同的階段,而每個階段都關注小的,相當簡單的任務
  • 這些階段放在一起構成了一個管道,在它的不同階段源文件都會向他的目標形式前進一步
  • 一般而言,翻譯任何語言的第一個階段是詞法分析,詞法分析是把源文件分解成組成它的詞
  • 在分離和提取單詞之後,詞法分析器的真正工作是把單詞流轉變成屬性字流(Token stram)
  • 把單詞流轉換成屬性字流的過程叫做屬性字識別,因此詞法分析器也叫做屬性字識別器
//字符串流
Mov Sum, X; 執行加法運算
//單詞流
MOV SUM, X
//屬性字流
TOKEN_TYPE_INSTR
TOKEN_TYPE_IDENT
TOKEN_TYPE_COMMA
TOKEN_TYPE_IDENT

語法分析

  • 在管道中語法分析器緊跟在詞法分析器和屬性字識別器後,並且有一個非常重要的任務
  • 給定一個屬性字流,當把它們作爲整體單元時,語法分析器負責把它們拼湊在一起
  • 對於函數聲明的基本語法分析過程
Token CurrToken = GetNextToken();//從屬性字流中讀取下一個屬性字
if (CurrToken == TOKEN_TYPE_FUNC) //是否是一個函數聲明的開始
{
	if (GetNextToken() == TOKEN_TYPE_IDENT)
	{
		string FuncName = GetCurrLexeme(); //當前的單詞是函數名 所以保存他
		if (GetNextToken() == TOKEN_TYPE_OPEN_BRACKET) {
			//正確的屬性字流
		}
	}
}
  • 在對一個指令進行了語法分析之後,就可以使用指令查找標來驗證它的操作數並把它轉換成機器碼

字符串處理庫

  • 空白符 空白符可以存在於任何一個字符串中,它通常被簡單地定義爲不可見的字符比如說空格,製表符,和和換行符
  • 區分空白符是否包含換行符是很重要的。對於語句可以跨越多行的C語言中,換行沒有意義,空白可以包含空白符
  • 字符串與字符可以通過許多方法進行分組和分類,例如如果一個字符串的每個字符都獨立滿足數字字符條件,那麼這個字符串就可以被看作一個數字字符串
  • 你經常需要驗證各種各樣的字符串類型,範圍從標識符到浮點數再到單個的字符,比如說大括號或雙引號
  • 這也是決定單詞相應屬性字時的公用功能,字符串處理函數庫應該包含字符串分類函數的擴展集

字符串分類函數

  • 彙編器完成的時候,你會發現最頻繁需要的就是字符串分類函數
  • 一般來說,當你按照你的方式處理源代碼的時候,你需要了解所給的字符是否是下面幾種中的一種
  1. 數字字符
  2. 合法標識符中的字符
  3. 空白符 (空格或製表符)
  4. 分隔符(用來分隔元素的符號,如括號,逗號等)
#define TRUE 1;
#define FALSE 0;

//判定一個字符是否是數字字符
int IsCharNumeric(char cChar) {
	if (cChar >= '0' && cChar <= '9')
	{
		return TRUE;
	}
	else { return FALSE; }
}

//判定一個字符是否是空白符
int IsCharWhitespace(char cChar) {
	if (cChar == ' ' || cChar == "\t")
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

//判定一個字符是否是有效標識符的部分
int IsCharIdent(char cChar) {
	if ((cChar >= '0' && cChar <= '9') ||
		(cChar >= 'A' && cChar <= 'Z') ||
		(cChar >= 'a' && cChar <= 'z') ||
		cChar == '_')
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

int IsCharDelimiter(char cChar) {

	if (cChar == ';' || cChar == ',' || cChar == '"' || cChar == '[' || cChar == ']' || cChar == '{' || cChar == '}' || IsCharWhitespace(cChar))
	{
		return TRUE;
	}
	else {
		return FALSE;
	}

}

int IsStringInt(char* pstrString) {

	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return FALSE;
	unsigned int iCurrCharIndex;

	if (!IsCharNumeric(pstrString[0]) && !pstrString[0] == '-') return FALSE;
	
	for (iCurrCharIndex = 1; iCurrCharIndex < strlen(pstrString); ++iCurrCharIndex)
	{
		if (!IsCharNumeric(pstrString[iCurrCharIndex]))
		{
			return FALSE;
		}
	}
		
	return TRUE;
}

int IsStringFloat(char* pstrString) {
	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return FALSE;

	unsigned int iCurrCharIndex;

	for (iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (!IsCharNumeric(pstrString[iCurrCharIndex])&&!(pstrString[iCurrCharIndex]=='.')&&!(pstrString[iCurrCharIndex]=='-'))
		{
			return FALSE;
		}
	}

	//小數點
	int iRadixPointFound = FALSE;
	for (iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (pstrString[iCurrCharIndex]=='.')
		{
			if (iRadixPointFound) { return FALSE; }
			else
			{
				iRadixPointFound = TRUE;
			}
		}
	}

	for (iCurrCharIndex = 1; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (pstrString[iCurrCharIndex]=='-')
		{
			return FALSE;
		}
	}

	if (iRadixPointFound) {
		return TRUE;
	}
	else {
		return FALSE;
	}

}

int IsStringWhitespace(char* pstrString) {
	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return TRUE;


	for (unsigned int iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (!IsCharWhitespace( pstrString[iCurrCharIndex]))
		{
			return FALSE;
		}
	}

	return TRUE;
}

int IsStringIdent(char* pstrString) {
	if (!pstrString) return FALSE;
	if (strlen(pstrString) == 0) return FALSE;

	if (pstrString[0] >= '0' && pstrString[0] <= '9') return FALSE;

	for (unsigned int iCurrCharIndex = 0; iCurrCharIndex < strlen(pstrString); iCurrCharIndex++)
	{
		if (!IsCharIdent(pstrString[iCurrCharIndex]))
		{
			return FALSE;
		}
	}
	return TRUE;
}

彙編程序

  • 彙編程序主要是由管理各種腳本所定義元素如變量,函數和標籤的表組成
  • 由於各個腳本之間的這些元素的數量都各不相同,對於他們當中的大部分表,可以使用鏈表來增長到需要的大小
  • 我決定在內存中緩存所有的東西,這樣會使處理過程更快速
  • 緩衝將要輸出的彙編指令流,

詞法分析器的接口

  • int GetNextToken()
  • 返回當前節點的屬性字並把當前節點向後移動一個節點,還要填入g_Lexer結構來反映所有的當前屬性字的信息
  • 當我們對單詞進行分析的時候,因爲分隔符本身也是屬性字的一種,所以對於Index0,當遍歷到其不是空白符或者製表符的時候就停止,然後讓Index1等於Index0,再讓Index1進行累加遍歷,如果此時Index1遇到分隔符就停止,這樣就可以分離出來分隔符。但是其長度爲0,所以要讓index1加一。
  • 不過此時會有一個bug," " "
  • char * GetCurrLexeme()
  • 返回一個字符指針,它指向包含當前單詞的字符串,什麼是g_Tokenizer
  • GetLookAheadChar()
  • 向前查看,查看位於當前屬性字之後的那個屬性字的處理過程。雖然他的確讀取了字符,但是它並沒有把屬性字流的當前指針向後移動。
  • 如果當前讀取內容不足以讓你正確決定餘下的屬性字應該是什麼,在這些情況下使用向前查看Var MyVar Var MyVar[256] //不確定性
  • void SkipToNextLine()
  • 僅僅想忽略掉一整行的屬性字,源代碼在內部存儲的時候被看作一系列單獨的行,這個函數就是增加當前行的計數並且重置屬性字識別器的位置
  • ResetLexer()
  • 重置一切,詞法分析器要對源代碼執行兩次遍歷,每次執行之前都需要重置,這個函數只會被使用兩次

錯誤處理

  • 錯誤處理主要包括三個方面,檢測,重新同步和消息輸出
  • 檢測是判斷錯誤是什麼時候發生的,它是什麼類型的錯誤
  • 重新同步是使語法分析器回滾的處理過程,讓程序可以標記多重錯誤
  • 錯誤消息必須輸出到屏幕或是某種類型的日誌文件

語法分析

  • 重點在於識別初始的屬性字,並且根據那個初始的屬性字是如何適合語言的規則的,
  • 來預知它後面應該跟隨什麼屬性字
  • 根據這些初始的屬性字,你可以判斷你正在處理什麼種類的代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章