出于对指针操作的支持,变量Variable需要模拟其在内存空间中的表现。每个变量都有个Address属性。参考Variable.Assign()方法,可知端倪:
public virtual void Assign(Expression.Operand.Value value) { if (this.TypeInfo.IsPointer) { Memory.SetInt(Address, value.AsInt); } else switch (TypeInfo.BaseType) // Assign value { case PrimitiveDataType.CharType: { Memory.SetChar(Address, value.AsChar); } break; case PrimitiveDataType.ShortType: { Memory.SetShort(Address, value.AsShort); } break; case PrimitiveDataType.IntType: { Memory.SetInt(Address, value.AsInt); } break; case PrimitiveDataType.FloatType: { Memory.SetFloat(Address, value.AsFloat); } break; default: throw new RuntimeException(string.Format("Uncompatible type between {0} and {1}.", TypeInfo.ToString(), value.GetTypeInfo(this).ToString())); // Uncompatible type } // switch }
对C的模仿,导致以上行为:赋值对原生类型而言,是将值写入地址空间,而指针则写入一个地址(32位)。
故此,在获取变量值时,值类型和指针类型有不同表现:
public virtual Expression.Operand.Value GetValue() { if (TypeInfo.IsPointer) { return new Expression.Operand.Value(PrimitiveDataType.IntType, Address); } object data; switch (TypeInfo.BaseType) { case PrimitiveDataType.CharType: data = Context.Memory.GetChar(Address); break; case PrimitiveDataType.ShortType: data = Context.Memory.GetShort(Address); break; case PrimitiveDataType.IntType: data = Context.Memory.GetInt(Address); break; case PrimitiveDataType.FloatType: data = Context.Memory.GetFloat(Address); break; default: return null; } return new Expression.Operand.Value(TypeInfo.BaseType, data); }
对于常量,SharpC仅将字符串视为常量,其他值则就地产生一个Expression.Operand.Value对象。
字符串常量产生于语法分析器探测到一对“”及其内容时,通过Parser.GetStringValue()方法获得。当得到一个字符串时,就产生一个类型为字符指针的Variable变量,再产生一个ConstantInitialize对象。ConstantInitialize对象包含了前一步所产生的Variable之名字以及初始化数据,在Run()方法被调用时根据这些信息初始化该变量。Variable和ConstantInitialize成对出现。代码片断如下:
// The const variable Variable stxConstVar = new Variable() { TypeInfo = new DataTypeInfo() { Type = PrimitiveDataType.CharType | PrimitiveDataType.PointerType, PointerCount = 1 }, ReferenceCount = 1 }; stxConstVar.Name = Context.GetAnonymousName(stxConstVar.TypeInfo); int byteArrayLen = Encoding.ASCII.GetByteCount(strBuilder.ToString()) + 1; ConstantInitialize stxConstInit = new ConstantInitialize() { Name = Context.GetAnonymousName("const_init"), VariableName = stxConstVar.Name, InitialValue = new byte[byteArrayLen] }; char[] charArray = strBuilder.ToString().ToCharArray(); Encoding.ASCII.GetBytes(charArray, 0, charArray.Length, stxConstInit.InitialValue, 0); stxConstInit.InitialValue[byteArrayLen - 1] = 0; ctx.GlobalContex.AddChildAtFirst(stxConstInit); ctx.GlobalContex.AddChildAtFirst(stxConstVar);
对于函数,参数在BeforeRun()方法中初始化一次。变参函数则次次都需要初始化。
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++; }
AllocateFixedArguments方法初始化参数:
private void AllocateFixedArguments(Context ctx) { // Allocate fixed arguments if (!IsVariableArgument) { IEnumerator<Context> argEnumerator = ArgumentDefinitions.GetEnumerator(); while (argEnumerator.MoveNext()) { (argEnumerator.Current as FunctionArgumentDefine).Run(this); } } }
函数参数其实就是Variable的重命名。当然在C#中没有重命名这回事,而是继承:
public class FunctionArgumentDefine : Variable { }