1. 程式人生 > >使用Kernel FP API實現一個執行Kernel FP程式碼的控制檯程式

使用Kernel FP API實現一個執行Kernel FP程式碼的控制檯程式

    說到底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