java開發C語言編譯器:為C語言提供API呼叫
更詳細的講解和程式碼除錯演示過程,請參看視訊
用java開發C語言編譯器
我們第一次使用C語言開發程式時,往往是在控制檯上列印一句”Hello World”,實現列印語句功能的函式是printf, 這個函式是有C語言的連結庫提供的,開發者可以直接呼叫,類似於這種無需自己實現,直接可以呼叫的函式,我們都稱為庫函式,或是API, 本節,我們要為當前構建的虛擬機器提供C語言庫函式,我們要給直譯器提供一種函式呼叫機制,這些函式無需程式自己實現,而是由我們的直譯器提供的,C語言程式直接呼叫即可,這節,我們要實現的一個庫函式就是printf. 完成本節程式碼後,我們的直譯器能解釋執行下面程式:
void main() {
printf("a is %d:",1);
}
printf函式是我們直譯器提供給程式碼的,有了庫函式,程式的開發便能高效很多。
庫函式機制的實現由新構造的類ClibCall,我們先看看它的實現程式碼:
package backend;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class ClibCall {
private Set<String> apiSet;
private ClibCall() {
apiSet = new HashSet<String>();
apiSet.add("printf");
}
private static ClibCall instance = null;
public static ClibCall getInstance() {
if (instance == null) {
instance = new ClibCall();
}
return instance;
}
public boolean isAPICall(String funcName) {
return apiSet.contains(funcName);
}
public Object invokeAPI(String funcName) {
switch (funcName) {
case "printf":
return handlePrintfCall();
default:
return null;
}
}
private Object handlePrintfCall() {
ArrayList<Object> argsList = FunctionArgumentList.getFunctionArgumentList().getFuncArgList(false);
String argStr = (String)argsList.get(0);
String formatStr = "";
int i = 0;
int argCount = 1;
while (i < argStr.length()) {
if (argStr.charAt(i) == '%' && i+1 < argStr.length() &&
argStr.charAt(i+1) == 'd') {
i += 2;
formatStr += argsList.get(argCount);
argCount++;
} else {
formatStr += argStr.charAt(i);
i++;
}
}
System.out.println(formatStr);
return null;
}
}
ClibCall 包含了一個集合類叫apiSet, 其用於儲存庫函式的函式名,當程式碼中有函式呼叫時,直譯器拿到被調函式的名字,提交給ClibCall, 該類會在apiSet中查詢是否含有與給定名字相同的字串,如果有,那意味著該函式屬於庫函式。
由於目前我們只實現了一個庫函式printf, 因此初始化時,我們把字串”printf”加入到該集合中。 isAPICall 傳入的是函式名,如果函式名包含在apiSet裡面,那麼返回true, 表明他是庫函式呼叫。
invokeAPICall 用來執行給定的庫函式,傳入引數是庫函式的名稱。在裡面,直譯器根據不同的庫函式名稱,去實現不同的庫函式。handlePrintfCall用於實現printf呼叫,首先,它獲得輸入引數,第一個輸入引數是要顯示到控制檯上的字串,在字串中,可能會含有格式化字元,當前我們實現的printf可接受的格式化字元是%d. 在printf的模擬實現中,我們遍歷每一個字元,當遇到%d時,我們從引數列表中獲得對應的數值,然後把數值替換%d格式符,最後通過println把格式化後的字串列印到控制檯上。
有了庫函式呼叫後,每當直譯器解析到函式呼叫是,需要確定當前呼叫的函式是程式碼自己實現的,還是庫函式提供的,這個機制的實現在UnaryExecutor中,程式碼如下:
public class UnaryNodeExecutor extends BaseExecutor{
@Override
public Object Execute(ICodeNode root) {
executeChildren(root);
int production = (Integer)root.getAttribute(ICodeKey.PRODUCTION);
String text ;
Symbol symbol;
Object value;
ICodeNode child;
switch (production) {
....
case CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary:
//先獲得函式名
String funcName = (String)root.getChildren().get(0).getAttribute(ICodeKey.TEXT);
if (production == CGrammarInitializer.Unary_LP_ARGS_RP_TO_Unary) {
ICodeNode argsNode = root.getChildren().get(1);
ArrayList<Object> argList = (ArrayList<Object>)argsNode.getAttribute(ICodeKey.VALUE);
FunctionArgumentList.getFunctionArgumentList().setFuncArgList(argList);
}
//找到函式執行樹頭節點
ICodeNode func = CodeTreeBuilder.getCodeTreeBuilder().getFunctionNodeByName(funcName);
if (func != null) {
Executor executor = ExecutorFactory.getExecutorFactory().getExecutor(func);
executor.Execute(func);
Object returnVal = func.getAttribute(ICodeKey.VALUE);
if (returnVal != null) {
System.out.println("function call with name " + funcName + " has return value that is " + returnVal.toString());
root.setAttribute(ICodeKey.VALUE, returnVal);
}
} else {
ClibCall libCall = ClibCall.getInstance();
if (libCall.isAPICall(funcName)) {
Object obj = libCall.invokeAPI(funcName);
root.setAttribute(ICodeKey.VALUE, obj);
}
}
....
}
當直譯器解析函式呼叫時,它現在函式呼叫表中,查詢給定函式的語法執行樹頭結點,如果找不到的話,那麼直譯器知道,這個函式是庫函式,於是呼叫ClibCall來處理,它先通過isAPICall來判斷,給定函式是否是庫函式,如果是的話,則呼叫invokeAPI來執行庫函式提供的功能。
有了ClibCall, 以後我們想要新增新的庫函式時,只要修改ClibCall的實現即可,基於現在的架構基礎上,我們今後可以快速的實現更多庫函式,從而讓我們的直譯器越來越強大!
更加具體的程式碼解釋和除錯過程請參看視訊。
更多技術資訊,包括作業系統,編譯器,面試演算法,機器學習,人工智慧,請關照我的公眾號:
相關文章
- 【開發語言】PHP、Java、C語言的編譯執行過程PHPJavaC語言編譯
- C語言編譯器開發之旅(二):解析器C語言編譯
- C語言編譯器手機版C語言編譯
- C語言 - 條件編譯C語言編譯
- c語言多檔案編譯C語言編譯
- C語言編譯器開發之旅(一):詞法分析掃描器C語言編譯詞法分析
- go語言與c語言的相互呼叫GoC語言
- 使用 Sublime Text 3 編譯 C 語言編譯
- 3- C語言編譯過程C語言編譯
- 編譯warp,d語言寫的c/c++前處理器.編譯C++
- Java如何呼叫C語言程式,JNI技術JavaC語言
- C語言函式呼叫棧C語言函式
- 源語言、目標語言、翻譯器、編譯器、直譯器編譯
- Linux下C語言編譯的問題LinuxC語言編譯
- 【C語言】linux下多檔案編譯C語言Linux編譯
- 現代編譯原理C語言描述pdf編譯原理C語言
- C語言-->(十四)結構體、巨集、編譯C語言結構體編譯
- Android-NDK-11-C語言編譯原理AndroidC語言編譯原理
- 組合語言-019(彙編程式與c\c++相互呼叫)組合語言C++
- Notepad++編譯和執行C語言 (GCC)編譯C語言GC
- C語言C語言
- 何為程式語言?為什麼要學C語言?C語言
- 【軟體開發底層知識修煉】五 gcc-C語言編譯器GCC語言編譯
- 聊聊C語言/C++—程式和程式語言C語言C++
- 國產系統級程式語言與編譯器,輕鬆與 C 語言進行互動編譯
- C語言:發工資C語言
- C語言的編譯連結執行過程C語言編譯
- C語言編譯和連結過程簡介C語言編譯
- C語言 編寫線段樹C語言
- C語言字串C語言字串
- C語言(一)C語言
- C語言: returnC語言
- C語言 typedefC語言
- C語言練手專案--C 語言製作簡單計算器C語言
- C語言與嵌入式C語言的區別C語言
- C語言學習方法,怎麼學習C語言?C語言
- C語言程式碼區錯誤以及編譯過程C語言編譯
- 淺談,C語言編譯原理的個人見解C語言編譯原理
- 編譯型語言與解釋型語言編譯