设计一门脚本语言——(五)变量

本文详细介绍了如何在脚本中定义和使用变量,通过创建`Variable`类和`TValue`类,实现了变量的赋值、存储和访问。重点讨论了如何在表达式中利用变量,以及如何通过改进`Lexel`词法分析器来提高解析效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一章讲变量,可能内容多一点,因为涉及到的东西多了

要定义变量,我们需要先用一个类来表示我们在脚本中用到的值,因为是无类型的。所以建立一个类

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;

运行测试,看看每个变量的值,哈哈,能成功计算了,并且变量也添加到变量表中了

是不是感觉很简单,简单的搞定了变量在表达式中的运用。下一次做什么呢?想想看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值