The function is implemented as follows:
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; ...
The FindByName search method requires a list of parameters:
public override Context FindByName(string str) { if (ArgumentDefinitions != null) { foreach (FunctionArgumentDefine arg in ArgumentDefinitions) { if (arg.Name == str) return arg; } } return base.FindByName(str); }
The running method is as follows:
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": ""); // preparation BeforeRun (ctx, parameters); Run (ctx); // cleaning AfterRun (ctx );}
Preparations include:
1. initialize function parameters.
2. Set the function parameters using the input parameters (it seems like the logging interface ).
3. initialize the parameter stack for recursion.
Read the Code:
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++; }
The parameter initialization is performed only when the first call is made. In this case, apart from variable parameter functions, fixed parameter functions need to release these parameters at the end of the interpreter program. Please refer to the previous article ~ Context () code.
Before and After always appear in pairs:
IteratorCount--; if (IteratorCount > 0) { if (m_parameterStack.Count > 0) { RestoreParameter(ctx); } } else { // Clean variable arguments if (IsVariableArgument) { FreeVariableArgument(ctx); } }
Note that the previous parameter must be restored from recursive return.
Now, reload the Context. Run () method. Note: Because Forward Declaration is supported, the function body may be empty:
public override void Run(Context ctx) { if (Body != null) { Body.Run(this); } }
Now, the function structure has begun to take shape.
The following uses malloc as an example to illustrate how to implement an internal function:
Public class Malloc: FunctionDefine {public Malloc () {// init name Name = "malloc"; // Init return type ReturnType = new DataTypeInfo () {Type = PrimitiveDataType. voidType | PrimitiveDataType. pointerType, PointerCount = 1}; // initialize the parameter list ArgumentDefinitions. add (new FunctionArgumentDefine () {Name = "size", TypeInfo = new DataTypeInfo () {Type = PrimitiveDataType. intType | PrimitiveDataType. unsignedType}); // register to the global directory so that the analyzer can access the Context. registerInternalFunction (Name);} public override void Run (Context ctx) {FunctionArgumentDefine arg = ArgumentDefinitions. first () as FunctionArgumentDefine; ReturnValue = new Expression. operand. value () {DataType = PrimitiveDataType. intType, // allocate memory, with the address DataField = Context. memory. allocate (arg. getValue (). asInt )};}}
Let's look at the complicated print () method for printing output (). Note: This is a variable parameter function:
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; // indicates IsVariableArgument = true; Context. registerInternalFunction (Name);} public override void Run (Context ctx) {if (ArgumentDefinitions. count <1) return; // The first parameter should be of the string pointer type. It is the output template 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; // format the output 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; // output percentage case 'F': // floating point {if (argIdx <ArgumentDefinitions. count) {sb. append (ArgumentDefinitions [argIdx ++] as FunctionArgumentDefine ). getValue (). asFloat. toString () ;}} break; case 'I': // output INTEGER {if (argIdx <ArgumentDefinitions. count) {sb. append (ArgumentDefinitions [argIdx ++] as FunctionArgumentDefine ). getValue (). asInt. toString () ;}} break; case's ': // output string {if (argIdx <ArgumentDefinitions. count) {sb. append (Context. memory. getString (ArgumentDefinitions [argIdx ++] as FunctionArgumentDefine ). pointerAddress) ;}} break; case 'U': // The output unsigned integer {if (argIdx <ArgumentDefinitions. count) {sb. append (ArgumentDefinitions [argIdx ++] as FunctionArgumentDefine ). getValue (). asInt. toString ("{u}") ;}} break; case 'X': // output hexadecimal number 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: // unsupported format {sb. append ('%'); sb. append (ch);} break;} I ++;} else {// escape characters and other 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 ());}}}
The more complex one is the input () method. See its definition:
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); }
Six parameters are required, all of which are pointer types, and a return value is added. The execution process is as follows:
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 }; }
The following is a part of the execution of internal functions in C code:
int iVal = 0;if (input("Test Input", "Please input a string", "0", "%i", "Integer required", &iVal)){print("result=%i \n", iVal);}