函數的實現如下:
public class FunctionDefine : Context { private Stack<List<Expression.Operand.Value>> m_parameterStack; public DataTypeInfo ReturnType; public Expression.Operand.Operand ReturnValue; public bool IsVariableArgument; public int ReferenceCount; public int IteratorCount = 0; public List<Context> ArgumentDefinitions; public Block Body; ...
其尋找方法FindByName需要搜尋參數列表:
public override Context FindByName(string str) { if (ArgumentDefinitions != null) { foreach (FunctionArgumentDefine arg in ArgumentDefinitions) { if (arg.Name == str) return arg; } } return base.FindByName(str); }
其運行方法實現如下:
public virtual void Run(Context ctx, List<Expression.Operand.Operand> parameters) { Debug.WriteLine(string.Format("Call function \"{0}\" with [{1}] parameter{2}", Name, parameters.Count, parameters.Count > 0 ? "s" : "")); // 準備工作 BeforeRun(ctx, parameters); Run(ctx); // 清場工作 AfterRun(ctx); }
準備工作包括:
1. 初始化函數參數。
2. 使用傳入的參數設定函數參數(好像很拗口)。
3. 初始化參數棧,以備遞迴。
不多說,看代碼:
private void BeforeRun(Context ctx, List<Expression.Operand.Operand> parameters) { if (IsFirstRunning) { ConfigReturnEvent(ctx); AllocateFixedArguments(ctx); IsFirstRunning = false; } if (IsVariableArgument) { FreeVariableArgument(ctx); AllocateVariableArguments(ctx, parameters); } SavePreviousParameters(ctx); InitArguments(ctx, parameters); IteratorCount++; }
出效率計,僅在第一調用時進行參數初始化工作。這樣導致一個副作用就是除變參函數外,固定參數函數需要在解釋程式運行到最後時釋放這些參數。請參考前面的文章之~Context()代碼。
Before和After總是成對出現:
IteratorCount--; if (IteratorCount > 0) { if (m_parameterStack.Count > 0) { RestoreParameter(ctx); } } else { // Clean variable arguments if (IsVariableArgument) { FreeVariableArgument(ctx); } }
注意,從遞迴返回時要恢複前次的參數。
現在,重載Context.Run()方法。注意:由於支援前向申明,所以函數體可能為空白:
public override void Run(Context ctx) { if (Body != null) { Body.Run(this); } }
現而今,函數的結構已經初具。
下面,以malloc為例,看看怎麼實現一個內建函式:
public class Malloc : FunctionDefine { public Malloc() { // init name Name = "malloc"; // Init return type ReturnType = new DataTypeInfo() { Type = PrimitiveDataType.VoidType | PrimitiveDataType.PointerType, PointerCount = 1 }; // 初始化參數列表 ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "size", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.IntType | PrimitiveDataType.UnsignedType } }); // 註冊到全域,使得分析器能夠訪問 Context.RegisterInternalFunction(Name); } public override void Run(Context ctx) { FunctionArgumentDefine arg = ArgumentDefinitions.First() as FunctionArgumentDefine; ReturnValue = new Expression.Operand.Value() { DataType = PrimitiveDataType.IntType, // 分配記憶體,給出地址 DataField = Context.Memory.Allocate(arg.GetValue().AsInt) }; } }
再看看複雜一點的用於列印輸出的方法print()。注意,這是個變參函數:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Diagnostics;namespace SharpC.Grammar.Function.InternalFunction{ public class Print : FunctionDefine { public Print() { Name = "print"; ReturnType.Type = PrimitiveDataType.VoidType; //表明變參身份 IsVariableArgument = true; Context.RegisterInternalFunction(Name); } public override void Run(Context ctx) { if (ArgumentDefinitions.Count < 1) return; // 第一個參數應該是字串指標類型,為輸出模板 FunctionArgumentDefine argFormat = ArgumentDefinitions.First() as FunctionArgumentDefine; string formatStr = Context.Memory.GetString(argFormat.PointerAddress);#if DEBUG Debug.Write(string.Format("print(\"{0}\"", formatStr)); for (int m = 1; m < ArgumentDefinitions.Count; m++) Debug.Write("," + (ArgumentDefinitions[m] as FunctionArgumentDefine).GetValue().ToString()); Debug.Write(")");#endif StringBuilder sb = new StringBuilder(); int len = formatStr.Length; int i = 0; int argIdx = 1; int argLen = ArgumentDefinitions.Count; // 格式化輸出 while(i < len) { char ch = formatStr[i]; if (ch == '%') { i++; if (i >= len) { sb.Append('%'); break; } ch = formatStr[i]; switch (ch) { case '%': sb.Append('%'); break;//輸出百分比符號 case 'f': //浮點數 { if (argIdx < ArgumentDefinitions.Count) { sb.Append((ArgumentDefinitions[argIdx++] as FunctionArgumentDefine).GetValue().AsFloat.ToString()); } } break; case 'i': // 輸出整數 { if (argIdx < ArgumentDefinitions.Count) { sb.Append((ArgumentDefinitions[argIdx++] as FunctionArgumentDefine).GetValue().AsInt.ToString()); } } break; case 's': // 輸出字串 { if (argIdx < ArgumentDefinitions.Count) { sb.Append(Context.Memory.GetString((ArgumentDefinitions[argIdx++] as FunctionArgumentDefine).PointerAddress)); } } break; case 'u': // 輸出不帶正負號的整數 { if (argIdx < ArgumentDefinitions.Count) { sb.Append((ArgumentDefinitions[argIdx++] as FunctionArgumentDefine).GetValue().AsInt.ToString("{u}")); } } break; case 'x': // 輸出16進位數 case 'X': { if (argIdx < ArgumentDefinitions.Count) { int res = (ArgumentDefinitions[argIdx++] as FunctionArgumentDefine).GetValue().AsInt; if (ch == 'x') sb.Append(res.ToString("{x}")); else sb.Append(res.ToString("{X}")); } } break; default: // 不支援的格式 { sb.Append('%'); sb.Append(ch); } break; } i++; } else { // 逸出字元及其它 switch (ch) { case '\\': { i++; if (i >= len) { // Invalid escape character break; } ch = formatStr[i]; switch (ch) { case 'a': sb.Append('\a'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 'n': sb.Append('\n'); break; case 't': sb.Append('\t'); break; case 'v': sb.Append('\v'); break; case '"': sb.Append('"'); break; case '\\': sb.Append('\\'); break; default: { sb.Append('\\'); sb.Append(ch); } break; } i++; } break; default: sb.Append(ch); i++; break; } } } // while Debug.WriteLine(" output:{{" + sb.ToString() + "}}"); Console.Write(sb.ToString()); } }}
更複雜一點的,是input()方法。看其定義:
public class Input : FunctionDefine { public Input() { Name = "input"; ReturnType.Type = PrimitiveDataType.IntType; ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "title", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 } }); ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "message", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 } }); ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "defValue", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 } }); ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "format", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 } }); ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "errMsg", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 } }); ArgumentDefinitions.Add(new FunctionArgumentDefine() { Name = "result", TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 } }); Context.RegisterInternalFunction(Name); }
需要6個參數,全部是指標類型,再加一個傳回值。其執行處理如下:
public override void Run(Context ctx) { Function.FunctionArgumentDefine argTitle = ArgumentDefinitions[0] as Function.FunctionArgumentDefine; Function.FunctionArgumentDefine argMsg = ArgumentDefinitions[1] as Function.FunctionArgumentDefine; Function.FunctionArgumentDefine argDefVal = ArgumentDefinitions[2] as Function.FunctionArgumentDefine; Function.FunctionArgumentDefine argFormat = ArgumentDefinitions[3] as Function.FunctionArgumentDefine; Function.FunctionArgumentDefine argErrMsg = ArgumentDefinitions[4] as Function.FunctionArgumentDefine; Function.FunctionArgumentDefine argResult = ArgumentDefinitions[5] as Function.FunctionArgumentDefine; if (argResult.Address == 0) throw new RuntimeException(string.Format("Parameter \"{0}\" is invalid: {1}.", argResult.Name, argResult.Address)); InputForm inputFrm = new InputForm(); inputFrm.Title = argTitle.Address != 0 ? Context.Memory.GetString(argTitle.PointerAddress) : string.Empty; inputFrm.Message = argMsg.Address != 0 ? Context.Memory.GetString(argMsg.PointerAddress) : string.Empty; inputFrm.DefaultValue = argDefVal.Address != 0 ? Context.Memory.GetString(argDefVal.PointerAddress) : string.Empty; inputFrm.Format = argFormat.Address != 0 ? Context.Memory.GetString(argFormat.PointerAddress) : string.Empty; inputFrm.ValidationMessage = argErrMsg.Address != 0 ? Context.Memory.GetString(argErrMsg.PointerAddress) : string.Empty; int resVal = 0; if (inputFrm.ShowDialog() == System.Windows.Forms.DialogResult.OK) { int address = argResult.PointerAddress; switch (inputFrm.Format) { case "%f": Context.Memory.SetFloat(address, float.Parse(inputFrm.Result)); break; case "%i": Context.Memory.SetInt(address, int.Parse(inputFrm.Result)); break; case "%u": Context.Memory.SetInt(address, (int)uint.Parse(inputFrm.Result)); break; case "%x": Context.Memory.SetInt(address, (int)uint.Parse(inputFrm.Result)); break; case "%c": Context.Memory.SetChar(address, (byte)inputFrm.Result[0]); break; case "%s": default: Context.Memory.SetString(address, inputFrm.Result); break; } resVal = 1; } ReturnValue = new Expression.Operand.Value() { DataType = PrimitiveDataType.IntType, DataField = resVal }; }
另附在C代碼中執行內建函式的片斷:
int iVal = 0;if (input("Test Input", "Please input a string", "0", "%i", "Integer required", &iVal)){print("result=%i \n", iVal);}