这一章讲变量,可能内容多一点,因为涉及到的东西多了
要定义变量,我们需要先用一个类来表示我们在脚本中用到的值,因为是无类型的。所以建立一个类
TValue来表示
public class TValue
{
// 变量类型
public enum TValueType
{
None, // 未知
Real, // 数字
String // 字符串
}
public static readonly TValue Empty = new TValue(string.Empty, TValueType.None);
public string Value;
public TValueType VType;
public TValue()
{
Value = string.Empty;
VType = TValueType.None;
}
public TValue(string value, TValueType type)
{
this.Value = value;
this.VType = type;
}
public static bool operator ==(TValue val1, TValue val2)
{
return val1.VType == val2.VType && val1.Value == val2.Value;
}
public static bool operator !=(TValue val1, TValue val2)
{
return val1.VType != val2.VType || val1.Value != val2.Value;
}
public static TValue operator +(TValue val1, TValue val2)
{
if (TypeEquals(val1, val2) && val1.VType == TValueType.Real)
{
return new TValue((float.Parse(val1.Value) + float.Parse(val2.Value)).ToString(), TValueType.Real);
}
else
{
return new TValue(val1.Value.ToString() + val2.Value.ToString(), TValueType.String);
}
}
public static TValue operator -(TValue val1, TValue val2)
{
if (TypeEquals(val1, val2) && val1.VType == TValueType.Real)
{
return new TValue((float.Parse(val1.Value) - float.Parse(val2.Value)).ToString(), TValueType.Real);
}
else
{
return new TValue(val1.Value, val1.VType);
}
}
public static bool TypeEquals(TValue val1, TValue val2)
{
return val1.VType == val2.VType;
}
}
代码虽然有一点多,但意思很简单,需要注意的是不同类型的值,最算术运算的时候的处理方式
这里简单的处理,我这里对于String型,加法是连接字符串
对于String+Real型,是把Real转化为String在连接
乘法和除法的重载,是类似的,没有给出。
一般,String * Real是不行的。
当然,大家也可以发挥创意,比如,在乘法重载函数中
把String * Real的值,当做把String重复Real次也可以啊(当然,需要注意Real为浮点数的情况)
好了,有了TValue,我们就可以创建Variable类
首先我们考虑一下变量有哪些属性
1.肯定有值TValue;
2.需要有个名字Name
3.还有全局变量、类变量、局部变量(这个可以建立Function类Class类,然后把Variable当做它们的子类来实现)
public class Variable
{
public string Name = string.Empty;
public TValue Value = TValue.Empty;
}
开始分析变量之前,我们改进一下Lexel词法分析器
很多时候,我们需要判断下一个Token是什么
比如,def表示定义一个东西,可是如果后面是“_”表示定义类,是“:”表示定义函数
在读取到def后,我们需要偷看一下,下一个Token是什么,所以加入函数Lexel.PeekNext();
public TokenType PeekNext()
{
TokenType type = GetToken();
if (type != TokenType.End)
{
Rewind();
}
return type;
}
比较简单,但功能很实用。
另外,有时候我们还需要读取指定Token,比如在定义变量的时候
我们读取到@后,接下来要读取一个Ident,因为只有Ident能作为变量名
其余,如Real,Opeator和Key都不合理,没有@12=1的语法
所以定义函数Lexel.GetToken(TokenType type);
当然,你可以把这个函数叫做Lexel.GetExpecteToken(TokenType type);
但它其实和GetToken做一样的工作,所以我用了函数重载
public void GetToken(TokenType type)
{
try
{
if (GetToken() != type)
{
throw new Exception("Unexpect " + type.ToString());
}
}
catch (Exception ex)
{
throw ex;
}
}
功能很简单,但省了我们判读很多错误的时间
就是判断GetToken是否等于指定TokenType,不是就抛出异常
好了,现在开始分析变量定义(@X = 10;之类)
为了简单起见,而且我们也没有开始分析函数和类,所以变量都当做全局变量
新建一个GlobalData类,用来存储所有的变量,包括类,函数,变量等
public class GlobalData
{
private List<Variable> vars = null;
public void AddVariable(sstring varName, TValue value)
{
vars.Add(new Variable(varName, value));
}
然后再Parse中定义结构体,用来表示一个脚本会用到的所有东西
public struct InstanceData
{
public TValue ExpValue; 表达式的值
public int ExpLayer; 表达式的层数
public GlobalData GD; 存储所有数据
}
private InstanceData Ins; // 运行的实例
可能定义的东西有点多,但为了以后方便(其实在写之前,没弄结构化,后来重写了~~~)
加入分析变量的函数ParseVariable
private void ParseVariable()
{
try
{
lexel.GetToken(TokenType.Ident);
string name = lexel.CurrToken;
TValue value = TValue.Empty;
if (lexel.PeekNext() == TokenType.Equal) // 赋值
{
lexel.GetToken(TokenType.Equal);
ParseExp(true);
value = Ins.ExpValue;
}
lexel.GetToken(TokenType.Semicolon);
Ins.GD.AddVariable(name, value);
}
catch (Exception ex)
{
throw ex;
}
}
这个函数完成变量的定义,已经初始化(如果有的话)
下面让变量可以用在表达式中。前面说过,变量也是一个“因子”
那就在ParseFactor中加入
case TokenType.Ident: // 变量
Ins.ExpValue = Ins.GD.GetVar(lexel.CurrToken).Value;
break;
这里用到一个函数Ins(InstanceData).GD(GlobalData).GetVar()这个函数的功能是取出指定变量的值
目前这个函数的代码有点长,因为考虑了函数和类变量,但我们这里不考虑,所以删减点
public Variable GetVar(string varName)
{
try
{
for (int i = 0; i < vars.Count; i++)
{
if (vars[i].Name == varName)
{
return vars[i];
}
}
return null;
}
catch (Exception ex)
{
throw ex;
}
}
好像删减的有点多了,没事,能用就好,一步步来。
在test.txt中写入
@x=10;
@y=20;
@z = x + y + 10;
运行测试,看看每个变量的值,哈哈,能成功计算了,并且变量也添加到变量表中了
是不是感觉很简单,简单的搞定了变量在表达式中的运用。下一次做什么呢?想想看