使用Kernel FP API實現一個執行Kernel FP程式碼的控制檯程式
阿新 • • 發佈:2018-12-27
說到底Kernel FP是一個指令碼引擎,所以是需要API的。接下來的程式碼用來載入一些Kernel FP程式碼檔案,並執行main函式。
首先,我們讀入的檔案是一個目錄,目錄記錄了一個Kernel FP程式所需要的所有程式碼檔案:Project.txt
1 Startup.txt
2 SysUtils.txt
3 List.txt
讀入檔案之後將檔案按行分割:
1 VUnicodeString TestDataPath=VFileName(GetConsole()->GetAppPath()).MakeAbsolute(L"..\\TestData\\").GetStrW();
2 VUnicodeString TestOutput;
3 VL_UniStrings CodeFiles;
4 {
5 VL_FileStream Stream(TestDataPath+L"Project.txt",VL_FileStream::vfomRead);
6 VUnicodeString Project=ReadText(&Stream);
7 CodeFiles.SetText(Project);
8 }
現在CodeFiles已經有3個檔案的檔名了,現在需要宣告一些Kernel FP物件,然後載入這些檔案。檔案在語法分析的時候出錯,程式需要打印出錯誤資訊:
1 VL_KfpSymbol Symbol;
2 Symbol.AddUnit(CreateConsoleUnit());
3 VBool ErrorOccurred=false;
4 for(VInt i=0;i<CodeFiles.GetCount();i++)
5 {
6 VL_FileStream Stream(TestDataPath+CodeFiles[i],VL_FileStream::vfomRead);
7 VUnicodeString TestCode=ReadText(&Stream);
8 9 VL_KfpError::List Errors;
10 Symbol.AddUnit(TestCode,Errors);
11 if(Errors.GetCount())
12 {
13 TestOutput+=L"檔案\""+VUnicodeString(CodeFiles[i])+L"\"含有語法錯誤:\r\n";
14 TestOutput+=ToString(Errors);
15 ErrorOccurred=true;
16 }
17 }
收集了所有程式碼檔案之後,需要先進行一次編譯,在內部生成執行時所需要的assembly。當然,API不允許外部訪問內部資訊:
1 if(!ErrorOccurred)
2 {
3 VL_KfpError::List Errors;
4 Symbol.PreCompile(Errors);
5 if(Errors.GetCount())
6 {
7 TestOutput+=L"生成符號表時發生錯誤\r\n";
8 TestOutput+=ToString(Errors);
9 ErrorOccurred=true;
10 }
11 }
如果到這一步仍然沒有問題的話,我們可以建立VL_KfpMachine了。這個Machine提供了訪問程式公佈的一些函式和物件。
1 if(!ErrorOccurred)
2 {
3 VL_KfpMachine::Ptr Machine=Symbol.CreateMachine();
4 RunProgram(Machine);
5 }
6 else7 {
8 GetConsole()->Write(TestOutput);
9 }
中間有一步出錯的話,控制檯視窗上會顯示所有錯誤資訊。在上面我們發現有一個ToString函式將一個錯誤列表轉換成字串:
1 VUnicodeString ToString(VL_KfpError::List& Errors)
2 {
3 VUnicodeString Result=L"";
4 for(VInt i=0;i<Errors.GetCount();i++)
5 {
6 Result+=L"錯誤["+VUnicodeString(i+1)+L"]\t模組:"+Errors[i]->Module+L"\t行號:"+VUnicodeString(Errors[i]->Token.LineInFile+1)+L"\r\n";
7 Result+=L"資訊:"+Errors[i]->Message+L"\r\n";
8 }
9 return Result;
10 }
接下來是RunProgram函數了。這個函式獲得startup.main,我們假設main函式是放在startup名稱空間下面的。當然,如果不確定名稱空間的話,我們可以通過其他豐富的查詢手段來獲得我們需要的函式:
1 void RunProgram(VL_KfpMachine::Ptr Machine)
2 {
3 ConsolePlugin Plugin;
4 Machine->AddPlugin(&Plugin,false);
5 6 VInt Index=0;
7 VInt ID=Machine->GetFunctionFirstIdByName(L"startup.main");
8 if(ID==-1)
9 {
10 break;
11 }
12 else13 {
14 VL_KfpValue MainFunction=Machine->CreateFunction(ID);
15 if(MainFunction.IsInvokable())
16 {
17 MainFunction=Machine->CreateEvaluableIO(MainFunction);
18 }
19 GetConsole()->Write(L"返回值:"+MainFunction.GetDebugString()+L"\r\n");
20 }
21 }
這裡面有兩個問題。控制檯程式是要有read和write的API的。所以我們這裡提供了兩個介面。首先,剛才在編譯Kernel FP之前有一句程式碼新增read和write的符號:
1 VL_KfpSymbol Symbol;
2 Symbol.AddUnit(CreateConsoleUnit());
CreateConsoleUnit程式碼如下:
1 KfpUnit CreateConsoleUnit()
2 {
3 KfpUnit Unit(L"console");
4 Unit.AddImport(L"system");
5 //func read :: IO string alias "KfpType::read" 6 Unit.AddFuncAlias(KfpDecl(L"read"),
7 KfpType(L"IO")<<KfpType(L"string"),
8 L"console::read" 9 );
10 //func write :: string -> IO void alias "KfpType::write"11 Unit.AddFuncAlias(KfpDecl(L"write"),
12 KfpType(L"string")>>(KfpType(L"IO")<<KfpType(L"void")),
13 L"console::write"14 );
15 //func writeln :: string -> IO void alias "KfpType::writeln"16 Unit.AddFuncAlias(KfpDecl(L"writeln"),
17 KfpType(L"string")>>(KfpType(L"IO")<<KfpType(L"void")),
18 L"console::writeln"19 );
20 return Unit;
21 }
有了這些符號還是不夠的。程式執行的過程中,呼叫了這些函式的話,仍然需要我們處理。所以我們還得實現一個外掛來執行這三個函式:
1 class ConsolePlugin : public VL_KfpPlugin
2 {
3 protected:
4 VInt FuncRead;
5 VInt FuncWrite;
6 VInt FuncWriteLine;
7 public:
8 ConsolePlugin()
9 {
10 FuncRead=-1;
11 FuncWrite=-1;
12 FuncWriteLine=-1;
13 }
14 15 void DestroyUserValue(VL_Base* Object)
16 {
17 }
18 19 void ConnectMachine()
20 {
21 FuncRead =RegisterExternalFunction(L"console::read", 1);
22 FuncWrite =RegisterExternalFunction(L"console::write", 2);
23 FuncWriteLine =RegisterExternalFunction(L"console::writeln", 2);
24 }
25 26 VUnicodeString GetName()
27 {
28 return L"Vczh KernelFP Demo Console Plugin";
29 }
30 31 VLE_KfpPluginResult Invoke(VInt ExternalID , InParams& In , OutParams& Out)
32 {
33 if(ExternalID==FuncWrite || ExternalID==FuncWriteLine)
34 {
35 if(In.Parameters.GetCount()==2)
36 {
37 VUnicodeString String;
38 if(In.Parameters[0].GetString(String))
39 {
40 if(ExternalID==FuncWrite)
41 {
42 GetConsole()->Write(String);
43 }
44 else45 {
46 GetConsole()->Write(String+L"\r\n");
47 }
48 Out.Result=GetMachine()->CreateIOSuccess(GetMachine()->CreateVoid());
49 return vkprSuccess;
50 }
51 elseif(In.Parameters[0].GetErrorMessage(Out.ErrorMessage))
52 {
53 return vkprFail;
54 }
55 }
56 Out.ErrorMessage=57 (ExternalID==FuncWrite?58 L"write函式的引數必須是一個string和一個IOEnv。":
59 L"writeln函式的引數必須是一個string和一個IOEnv。"60 );
61 return vkprFail;
62 }
63 if(ExternalID==FuncRead)
64 {
65 if(In.Parameters.GetCount()==1)
66 {
67 VUnicodeString String;
68 GetConsole()->Read(String);
69 Out.Result=GetMachine()->CreateIOSuccess(GetMachine()->CreateString(String));
70 return vkprSuccess;
71 }
72 else73 {
74 Out.ErrorMessage=L"read函式的引數必須是一個IOEnv。";
75 return vkprFail;
76 }
77 }
78 return vkprPass;
79 }
80 };
外掛在ConnectMachine的時候,註冊函式名和引數個數,然後返回一個ID供Invoke使用。跟IO有關的函式都是IO型別的。譬如read是IO string,而write是string->IO void。IO型別的函式都多了一個引數用於其他用途,所以read、write和writeln的引數個數才會是1、2和2。
Kernel FP API還提供了一些輔助函式用於建立或訪問IO物件。實際上只要外掛的行為與外掛所宣告的函式型別一致的話,基本上是不會有問題的。程式到這裡就結束了,讓我們看一看system(自動載入)模組與console(CreateConsoleUnit)模組生成的程式碼:
首先是system模組:
1 module system
2 3 type void 4 5 type int 6 7 type float 8 9 type char 10 11 data bool= (false|true)
12 13 data list T = (empty | (list T (list T)))
14 15 data maybe Ts Tf = ((success Ts) | (fail Tf))
16 17 type string= (list char)
18 19 data pair T1 T2 = (pair T1 T2)
20 21 data IOError = (ioemessage string)
22 23 type IOEnv
24 25 type IO T = (IOEnv -> (maybe (pair T IOEnv) IOError))
26 27 func iadd :: (int-> (int->int)) alias "kernelfp::iadd" 28 29 func isub :: (int-> (int->int)) alias "kernelfp::isub" 30 31 func imul :: (int-> (int->int)) alias "kernelfp::imul" 32 33 func idiv :: (int-> (int->int)) alias "kernelfp::idiv" 34 35 func imod :: (int-> (int->int)) alias "kernelfp::imod" 36 37 func igt :: (int-> (int->bool)) alias "kernelfp::igt" 38 39 func ilt :: (int-> (int->bool)) alias "kernelfp::ilt" 40 41 func iequ :: (int-> (int->bool)) alias "kernelfp::iequ" 42 43 func iegt :: (int-> (int->bool)) alias "kernelfp::iegt" 44 45 func ielt :: (int-> (int->bool)) alias "kernelfp::ielt" 46 47 func ineq :: (int-> (int->bool)) alias "kernelfp::ineq" 48 49 func fadd :: (float-> (float->float)) alias "kernelfp::fadd" 50 51 func fsub :: (float-> (float->float)) alias "kernelfp::fsub" 52 53 func fmul :: (float-> (float->float)) alias "kernelfp::fmul" 54 55 func fdiv :: (float-> (float->float)) alias "kernelfp::fdiv" 56 57 func fmod :: (float-> (float->float)) alias "kernelfp::fmod" 58 59 func fgt :: (float-> (float->bool)) alias "kernelfp::fgt" 60 61 func flt :: (float-> (float->bool)) alias "kernelfp::flt" 62 63 func fequ :: (float-> (float->bool)) alias "kernelfp::fequ" 64 65 func fegt :: (float-> (float->bool)) alias "kernelfp::fegt" 66 67 func felt :: (float-> (float->bool)) alias "kernelfp::felt" 68 69 func fneq :: (float-> (float->bool)) alias "kernelfp::fneq" 70 71 func isnan :: (float->bool) alias "kernelfp::isnan" 72 73 func isinf :: (float->bool) alias "kernelfp::isinf" 74 75 func chr :: (int->char) alias "kernelfp::chr" 76 77 func ord :: (char->int) alias "kernelfp::ord" 78 79 func cgt :: (char-> (char->bool)) alias "kernelfp::cgt" 80 81 func clt :: (char-> (char->bool)) alias "kernelfp::clt" 82 83 func cequ :: (char-> (char->bool)) alias "kernelfp::cequ" 84 85 func cegt :: (char-> (char->bool)) alias "kernelfp::cegt" 86 87 func celt :: (char-> (char->bool)) alias "kernelfp::celt" 88 89 func cneq :: (char-> (char->bool)) alias "kernelfp::cneq" 90 91 func ceil :: (float->float) alias "kernelfp::ceil" 92 93 func floor :: (float->float) alias "kernelfp::floor" 94 95 func trunc :: (float->float) alias "kernelfp::trunc" 96 97 func itof :: (int->float) alias "kernelfp::itof" 98 99 func ftoi :: (float->int) alias "kernelfp::ftoi"100 101 func abs :: (float->float) alias "kernelfp::abs"102 103 func exp :: (float->float) alias "kernelfp::exp"104 105 func ln :: (float->float) alias "kernelfp::ln"106 107 func lg :: (float->float) alias "kernelfp::lg"108 109 func sqr :: (float->float) alias "kernelfp::sqr"110 111 func pow :: (float-> (float->float)) alias "kernelfp::pow
首先,我們讀入的檔案是一個目錄,目錄記錄了一個Kernel FP程式所需要的所有程式碼檔案:Project.txt
1 Startup.txt
2 SysUtils.txt
3 List.txt
讀入檔案之後將檔案按行分割:
1 VUnicodeString TestDataPath=VFileName(GetConsole()->GetAppPath()).MakeAbsolute(L"..\\TestData\\").GetStrW();
2 VUnicodeString TestOutput;
3 VL_UniStrings CodeFiles;
5 VL_FileStream Stream(TestDataPath+L"Project.txt",VL_FileStream::vfomRead);
6 VUnicodeString Project=ReadText(&Stream);
7 CodeFiles.SetText(Project);
8 }
現在CodeFiles已經有3個檔案的檔名了,現在需要宣告一些Kernel FP物件,然後載入這些檔案。檔案在語法分析的時候出錯,程式需要打印出錯誤資訊:
1 VL_KfpSymbol Symbol;
3 VBool ErrorOccurred=false;
4 for(VInt i=0;i<CodeFiles.GetCount();i++)
5 {
6 VL_FileStream Stream(TestDataPath+CodeFiles[i],VL_FileStream::vfomRead);
7 VUnicodeString TestCode=ReadText(&Stream);
8 9 VL_KfpError::List Errors;
11 if(Errors.GetCount())
12 {
13 TestOutput+=L"檔案\""+VUnicodeString(CodeFiles[i])+L"\"含有語法錯誤:\r\n";
14 TestOutput+=ToString(Errors);
15 ErrorOccurred=true;
16 }
17 }
收集了所有程式碼檔案之後,需要先進行一次編譯,在內部生成執行時所需要的assembly。當然,API不允許外部訪問內部資訊:
1 if(!ErrorOccurred)
2 {
3 VL_KfpError::List Errors;
4 Symbol.PreCompile(Errors);
5 if(Errors.GetCount())
6 {
7 TestOutput+=L"生成符號表時發生錯誤\r\n";
8 TestOutput+=ToString(Errors);
9 ErrorOccurred=true;
10 }
11 }
如果到這一步仍然沒有問題的話,我們可以建立VL_KfpMachine了。這個Machine提供了訪問程式公佈的一些函式和物件。
1 if(!ErrorOccurred)
2 {
3 VL_KfpMachine::Ptr Machine=Symbol.CreateMachine();
4 RunProgram(Machine);
5 }
6 else7 {
8 GetConsole()->Write(TestOutput);
9 }
中間有一步出錯的話,控制檯視窗上會顯示所有錯誤資訊。在上面我們發現有一個ToString函式將一個錯誤列表轉換成字串:
1 VUnicodeString ToString(VL_KfpError::List& Errors)
2 {
3 VUnicodeString Result=L"";
4 for(VInt i=0;i<Errors.GetCount();i++)
5 {
6 Result+=L"錯誤["+VUnicodeString(i+1)+L"]\t模組:"+Errors[i]->Module+L"\t行號:"+VUnicodeString(Errors[i]->Token.LineInFile+1)+L"\r\n";
7 Result+=L"資訊:"+Errors[i]->Message+L"\r\n";
8 }
9 return Result;
10 }
接下來是RunProgram函數了。這個函式獲得startup.main,我們假設main函式是放在startup名稱空間下面的。當然,如果不確定名稱空間的話,我們可以通過其他豐富的查詢手段來獲得我們需要的函式:
1 void RunProgram(VL_KfpMachine::Ptr Machine)
2 {
3 ConsolePlugin Plugin;
4 Machine->AddPlugin(&Plugin,false);
5 6 VInt Index=0;
7 VInt ID=Machine->GetFunctionFirstIdByName(L"startup.main");
8 if(ID==-1)
9 {
10 break;
11 }
12 else13 {
14 VL_KfpValue MainFunction=Machine->CreateFunction(ID);
15 if(MainFunction.IsInvokable())
16 {
17 MainFunction=Machine->CreateEvaluableIO(MainFunction);
18 }
19 GetConsole()->Write(L"返回值:"+MainFunction.GetDebugString()+L"\r\n");
20 }
21 }
這裡面有兩個問題。控制檯程式是要有read和write的API的。所以我們這裡提供了兩個介面。首先,剛才在編譯Kernel FP之前有一句程式碼新增read和write的符號:
1 VL_KfpSymbol Symbol;
2 Symbol.AddUnit(CreateConsoleUnit());
CreateConsoleUnit程式碼如下:
1 KfpUnit CreateConsoleUnit()
2 {
3 KfpUnit Unit(L"console");
4 Unit.AddImport(L"system");
5 //func read :: IO string alias "KfpType::read" 6 Unit.AddFuncAlias(KfpDecl(L"read"),
7 KfpType(L"IO")<<KfpType(L"string"),
8 L"console::read" 9 );
10 //func write :: string -> IO void alias "KfpType::write"11 Unit.AddFuncAlias(KfpDecl(L"write"),
12 KfpType(L"string")>>(KfpType(L"IO")<<KfpType(L"void")),
13 L"console::write"14 );
15 //func writeln :: string -> IO void alias "KfpType::writeln"16 Unit.AddFuncAlias(KfpDecl(L"writeln"),
17 KfpType(L"string")>>(KfpType(L"IO")<<KfpType(L"void")),
18 L"console::writeln"19 );
20 return Unit;
21 }
有了這些符號還是不夠的。程式執行的過程中,呼叫了這些函式的話,仍然需要我們處理。所以我們還得實現一個外掛來執行這三個函式:
1 class ConsolePlugin : public VL_KfpPlugin
2 {
3 protected:
4 VInt FuncRead;
5 VInt FuncWrite;
6 VInt FuncWriteLine;
7 public:
8 ConsolePlugin()
9 {
10 FuncRead=-1;
11 FuncWrite=-1;
12 FuncWriteLine=-1;
13 }
14 15 void DestroyUserValue(VL_Base* Object)
16 {
17 }
18 19 void ConnectMachine()
20 {
21 FuncRead =RegisterExternalFunction(L"console::read", 1);
22 FuncWrite =RegisterExternalFunction(L"console::write", 2);
23 FuncWriteLine =RegisterExternalFunction(L"console::writeln", 2);
24 }
25 26 VUnicodeString GetName()
27 {
28 return L"Vczh KernelFP Demo Console Plugin";
29 }
30 31 VLE_KfpPluginResult Invoke(VInt ExternalID , InParams& In , OutParams& Out)
32 {
33 if(ExternalID==FuncWrite || ExternalID==FuncWriteLine)
34 {
35 if(In.Parameters.GetCount()==2)
36 {
37 VUnicodeString String;
38 if(In.Parameters[0].GetString(String))
39 {
40 if(ExternalID==FuncWrite)
41 {
42 GetConsole()->Write(String);
43 }
44 else45 {
46 GetConsole()->Write(String+L"\r\n");
47 }
48 Out.Result=GetMachine()->CreateIOSuccess(GetMachine()->CreateVoid());
49 return vkprSuccess;
50 }
51 elseif(In.Parameters[0].GetErrorMessage(Out.ErrorMessage))
52 {
53 return vkprFail;
54 }
55 }
56 Out.ErrorMessage=57 (ExternalID==FuncWrite?58 L"write函式的引數必須是一個string和一個IOEnv。":
59 L"writeln函式的引數必須是一個string和一個IOEnv。"60 );
61 return vkprFail;
62 }
63 if(ExternalID==FuncRead)
64 {
65 if(In.Parameters.GetCount()==1)
66 {
67 VUnicodeString String;
68 GetConsole()->Read(String);
69 Out.Result=GetMachine()->CreateIOSuccess(GetMachine()->CreateString(String));
70 return vkprSuccess;
71 }
72 else73 {
74 Out.ErrorMessage=L"read函式的引數必須是一個IOEnv。";
75 return vkprFail;
76 }
77 }
78 return vkprPass;
79 }
80 };
外掛在ConnectMachine的時候,註冊函式名和引數個數,然後返回一個ID供Invoke使用。跟IO有關的函式都是IO型別的。譬如read是IO string,而write是string->IO void。IO型別的函式都多了一個引數用於其他用途,所以read、write和writeln的引數個數才會是1、2和2。
Kernel FP API還提供了一些輔助函式用於建立或訪問IO物件。實際上只要外掛的行為與外掛所宣告的函式型別一致的話,基本上是不會有問題的。程式到這裡就結束了,讓我們看一看system(自動載入)模組與console(CreateConsoleUnit)模組生成的程式碼:
首先是system模組:
1 module system
2 3 type void 4 5 type int 6 7 type float 8 9 type char 10 11 data bool= (false|true)
12 13 data list T = (empty | (list T (list T)))
14 15 data maybe Ts Tf = ((success Ts) | (fail Tf))
16 17 type string= (list char)
18 19 data pair T1 T2 = (pair T1 T2)
20 21 data IOError = (ioemessage string)
22 23 type IOEnv
24 25 type IO T = (IOEnv -> (maybe (pair T IOEnv) IOError))
26 27 func iadd :: (int-> (int->int)) alias "kernelfp::iadd" 28 29 func isub :: (int-> (int->int)) alias "kernelfp::isub" 30 31 func imul :: (int-> (int->int)) alias "kernelfp::imul" 32 33 func idiv :: (int-> (int->int)) alias "kernelfp::idiv" 34 35 func imod :: (int-> (int->int)) alias "kernelfp::imod" 36 37 func igt :: (int-> (int->bool)) alias "kernelfp::igt" 38 39 func ilt :: (int-> (int->bool)) alias "kernelfp::ilt" 40 41 func iequ :: (int-> (int->bool)) alias "kernelfp::iequ" 42 43 func iegt :: (int-> (int->bool)) alias "kernelfp::iegt" 44 45 func ielt :: (int-> (int->bool)) alias "kernelfp::ielt" 46 47 func ineq :: (int-> (int->bool)) alias "kernelfp::ineq" 48 49 func fadd :: (float-> (float->float)) alias "kernelfp::fadd" 50 51 func fsub :: (float-> (float->float)) alias "kernelfp::fsub" 52 53 func fmul :: (float-> (float->float)) alias "kernelfp::fmul" 54 55 func fdiv :: (float-> (float->float)) alias "kernelfp::fdiv" 56 57 func fmod :: (float-> (float->float)) alias "kernelfp::fmod" 58 59 func fgt :: (float-> (float->bool)) alias "kernelfp::fgt" 60 61 func flt :: (float-> (float->bool)) alias "kernelfp::flt" 62 63 func fequ :: (float-> (float->bool)) alias "kernelfp::fequ" 64 65 func fegt :: (float-> (float->bool)) alias "kernelfp::fegt" 66 67 func felt :: (float-> (float->bool)) alias "kernelfp::felt" 68 69 func fneq :: (float-> (float->bool)) alias "kernelfp::fneq" 70 71 func isnan :: (float->bool) alias "kernelfp::isnan" 72 73 func isinf :: (float->bool) alias "kernelfp::isinf" 74 75 func chr :: (int->char) alias "kernelfp::chr" 76 77 func ord :: (char->int) alias "kernelfp::ord" 78 79 func cgt :: (char-> (char->bool)) alias "kernelfp::cgt" 80 81 func clt :: (char-> (char->bool)) alias "kernelfp::clt" 82 83 func cequ :: (char-> (char->bool)) alias "kernelfp::cequ" 84 85 func cegt :: (char-> (char->bool)) alias "kernelfp::cegt" 86 87 func celt :: (char-> (char->bool)) alias "kernelfp::celt" 88 89 func cneq :: (char-> (char->bool)) alias "kernelfp::cneq" 90 91 func ceil :: (float->float) alias "kernelfp::ceil" 92 93 func floor :: (float->float) alias "kernelfp::floor" 94 95 func trunc :: (float->float) alias "kernelfp::trunc" 96 97 func itof :: (int->float) alias "kernelfp::itof" 98 99 func ftoi :: (float->int) alias "kernelfp::ftoi"100 101 func abs :: (float->float) alias "kernelfp::abs"102 103 func exp :: (float->float) alias "kernelfp::exp"104 105 func ln :: (float->float) alias "kernelfp::ln"106 107 func lg :: (float->float) alias "kernelfp::lg"108 109 func sqr :: (float->float) alias "kernelfp::sqr"110 111 func pow :: (float-> (float->float)) alias "kernelfp::pow