仿查询分析器的C#计算器——2.记号对象

上一篇中提到了用树形结构来分析表达式并计算求值的思路。但对程序来说,输入的表达式只是一个字符串而已。要将表达式表示成树型结 构,首先必须可以将表达式分解成一个个节点,然后才可以由节点组成树。这里将树上的每一个节点称之为记号对象TokenRecord。

根据上面的分析得出,记号对象要求有一个存储自身值的变量,有自己特定的计算方法,还要能知道其下级的值。由此可以得出 TokenRecord的基本信息(略去非关键信息):

属性

Index:在表达式中的列号,int类型,出错时用于指示错误所在,从1开始。

Priority:优先级,int类型,在分析表达式的时候需要。

TokenValue:记号值,object类型

ChildList:下级列表,List对象,用来存储下级元素,实现树结构。

方法

Execute:执行该元素的操作,abstract方法。

SetChildCount:设置下级数量,虚方法,用于检查下级数量合法性,在构造函数中调用。对应有一个m_ChildCount字段,用于存储下级数 量。因为检查下级属于元素内部的任务,所以将m_ChildCount设置为protected,也没有对应的ChildCount属性。

SetPriority:设置优先级,虚方法,在构造函数中调用。

CheckChildCount:检查下级数量,在Execute中调用,保证表达式合法。

其中TokenValue属性使用object类型是因为这里支持字符串、数值和逻辑值的运算。在最开始设计的时候,曾经采用过两个字段string和 double类型来存储字符串和数值,逻辑值也用数值表示,后来改成object更简单了。

TokenRecord类的代码如下:

/// <summary>
    /// 记号对象
    /// </summary>
    /// <remarks>Author:Alex Leo;</remarks>
    public abstract class TokenRecord
    {
        #region 属性和字段

        //下级个数
        protected int m_ChildCount;

        private int m_Index;
        /// <summary>
        /// 列序号
        /// </summary>
        public int Index
        {
            get { return m_Index; }
        }

        /// <summary>
        /// 优先级,必须赋值
        /// </summary>
        protected int m_Priority;
        /// <summary>
        /// 优先级
        /// </summary>
        /// <returns></returns>
        public int Priority
        {
            get { return m_Priority; }
        }

        private int m_Length;
        /// <summary>
        /// 操作符长度
        /// </summary>
        public int Length
        {
            get { return m_Length; }
        }

        private Type m_TokenValueType;
        /// <summary>
        /// 记号值类型
        /// </summary>
        public Type TokenValueType
        {
            get { return m_TokenValueType; }
            set { m_TokenValueType = value; }
        }

        private object m_TokenValue;
        /// <summary>
        /// 记号值
        /// </summary>
        public object TokenValue
        {
            get { return m_TokenValue; }
            set { m_TokenValue = value; }
        }

        private List<TokenRecord> m_ChildList = new List<TokenRecord>();
        /// <summary>
        /// 下级列表
        /// </summary>
        public List<TokenRecord> ChildList
        {
            get { return m_ChildList; }
        }

        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="Index">序号</param>
        /// <param name="Length">自身长度</param>
        public TokenRecord(int Index, int Length)
        {
            this.m_Index = Index;
            this.m_Length = Length;
            this.SetPriority();
            this.SetChildCount();
        }

        #region 方法

        /// <summary>
        /// 重写ToString方法
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            //可以根据需要修改以显示不同的信息
            return this.GetType().Name + "_" + GetValueString() + "_" + TokenValueType.ToString();
        }

        /// <summary>
        /// 获取值的字符串表示
        /// </summary>
        /// <returns></returns>
        public string GetValueString()
        {
            return this.TokenValue.ToString();
        }

        /// <summary>
        /// 检查下级数量,必要时可以重写,因为有些Token的下级数量可以是一个区间
        /// </summary>
        /// <param name="ErrorInformation">下级数量不符时显示的错误信息</param>
        internal void CheckChildCount(string ErrorInformation)
        {
            if (this.m_ChildList.Count != this.m_ChildCount)
                throw new SyntaxException(this.m_Index, this.m_Length, ErrorInformation);
        }

        #region 必须重写的方法

        /// <summary>
        /// 执行代码
        /// </summary>
        public abstract void Execute();

        /// <summary>
        /// 设置下级数量
        /// </summary>
        protected abstract void SetChildCount();

        /// <summary>
        /// 设置优先级
        /// </summary>
        protected abstract void SetPriority();

        #endregion

        #endregion

        #region 转换记号值类型

        /// <summary>
        /// 将记号值转换为字符串类型
        /// </summary>
        internal string ChangeTokenToString()
        {
            string strValue;
            strValue = (string)(this.TokenValue = this.TokenValue.ToString());
            this.TokenValueType = typeof(string);
            return strValue;
        }

        /// <summary>
        /// 将记号值转换为数字类型
        /// </summary>
        /// <param name="ErrorInformation">无法转换成数字时显示的错误信息</param>
        internal double ChangeTokenToDouble(string ErrorInformation)
        {
            double dblValue;
            if (this.TokenValueType != typeof(double))
            {
                if (double.TryParse(this.TokenValue.ToString(), out dblValue))
                    this.TokenValueType = typeof(double);
                else
                    throw new SyntaxException(this.m_Index, this.m_Length, ErrorInformation);
            }
            else
            {
                dblValue = (double)this.TokenValue;
            }
            return dblValue;
        }

        /// <summary>
        /// 将记号值转换为逻辑值
        /// </summary>
        internal bool ChangeTokenToBoolean()
        {
            bool blnValue = false;
            if (this.TokenValueType == typeof(string))
            {
                switch (this.TokenValue.ToString().Trim().ToLower())
                {
                    case "true":
                        blnValue = (bool)(this.TokenValue = true);
                        break;
                    case "false":
                    case "":
                    default:
                        blnValue = (bool)(this.TokenValue = false);
                        break;
                }
                this.TokenValueType = typeof(bool);
            }
            else if (this.TokenValueType == typeof(double))
            {
                blnValue = (bool)((Convert.ToInt32(this.TokenValue) != 0) ? (this.TokenValue = true) : (this.TokenValue = false));
                //检查上一行代码是否错误
                this.TokenValueType = typeof(bool);
            }
            else if (this.TokenValueType == typeof(bool))
            {
                blnValue = (bool)this.TokenValue;
            }
            else
            {
            }

            return blnValue;
        }

        #endregion

    }//class TokenRecord

时间: 2024-11-02 19:43:01

仿查询分析器的C#计算器——2.记号对象的相关文章

仿查询分析器的C#计算器——3.词法分析

承接上一篇,这一篇讲如何把表达式转换成记号对象,这里就涉及到了编译原理中的词法分析.关于编译原理我不想多讲,毕竟我自己也不 怎么熟悉,现在只知道其中有个有限自动机的概念.不管什么概念,用代码实现才是最终目标. 因为不清楚字符串中到底包含什么字符,只能一个个字符进行处理,采用循环一次次向后取一个字符进行判断.这里建立一个TokenFactory 记号"工厂"类,由这个类负责对表达式进行分析并"生产"出TokenRecord对象.其中包括两个方法, LexicalAna

仿查询分析器的C#计算器——1.初步分析

计算器是很多编程初学者都做过的,从最简单的控制台程序开始,输入值,输入运算符,再输入值,得到一个结果.带界面的基本上是模仿 Windows操作系统的计算器,通过按钮输入值和运算符,然后给出运算结果.能不能直接输入表达式,或者能够对好几个表达式进行计算,或者 选择表达式中的一部分进行计算,如果输入错误能不能指出表达式中的错误位置?这样的要求类似于MS SQL的查询分析器,本系列文章将介绍 如何实现这样一个计算器.(这些内容已经涉及到编译原理,但本人并非计算机专业毕业,只是稍微看了一下编译原理的资料

仿查询分析器的C#计算器——5.计算求值

前面几篇文章介绍了各种分析过程,本篇作为完结篇,介绍如何调用之前实现的代码,如何实现多行表达式或者选择部分表达式进行运算, 以及如何定位错误. 本程序可以不需要UI界面,独立成一个模块.如果表达式分析与计算功能打包成一个dll,那入口只有一个,SyntaxAnalyse类.new一个 SyntaxAnalyse类之后,调用其中的Analyse方法,将要计算的运算表达式作为参数传递进去,返回一个顶级TokenRecord对象,再根据返回的 TokenRecord对象的值类型取得结果,整个计算过程就

仿查询分析器的C#计算器——4.语法分析

上一篇中介绍通过词法分析将表达式转换成TokenRecord对象列表.在第一篇中提到将表达式用树形结构表示,然后就可以很方便的从下级 节点取值计算了.那么如何将列表分析成一棵树的结构呢? 还是以例子来说明,比如3*7+56/8-2*5,分析成TokenRecord列表就是 记号对象 对应表达式 TokenValue 3 TokenMultiply * TokenValue 7 TokenPlus + TokenValue 56 TokenDivide / TokenValue 8 TokenMi

仿查询分析器的C#计算器——6.函数波形绘制

最近把计算器完善了一下,添加了变量的支持,添加了更多的函数,把逻辑短路操作也实现了,并修正了一些小错误.想起来以前在一本书 里看到过一个示例,输入函数表达式,就可以绘制函数的波形.最开始学VB的时候,就喜欢用函数来画图.再加上对电子技术有点兴趣,很多 波形都可以用函数来表示,很自然就想到用程序来模拟示波器显示波形.但是因为函数都需要在代码里面写死,如果需要新增函数或者进行修 改,需要修改程序代码再编译运行.既然现在可以做到对表达式进行计算,也可以支持变量,那么让变量的值变化就可以计算得到不同的值

SQL查询分析器清除被注入恶意病毒代码

在SQL查询分析器执行以下代码就可以了. 01.declare @t varchar(255),@c varchar(255) 02.declare table_cursor cursor for select a.name,b.name 03.from sysobjects a,syscolumns b ,systypes c 04.where a.id=b.id and a.xtype='u' and c.name 05.in ('char', 'nchar', 'nvarchar', 'v

在查询分析器中,用快捷键取得字段的列表。

查询分析|用快捷键 ALTER      procedure sp_getfields    @tablename varchar(100)  as    declare tmpCursor cursor for    select name from syscolumns where id = Object_ID(@tablename)      open tmpCursor    declare @fieldname varchar(50)    declare @sqlstr1 varch

在查询分析器中,通过SQLDMO来得到脚本

查询分析|脚本 --用以下脚本,可以得到任意对象的创建时的脚本,网上好像也流传有,但属于自己的东西用着还是感觉舒坦.--增加快捷键后,然后在查询分析器中按相应的快捷键就能显示对象的脚本.SET QUOTED_IDENTIFIER ON GOSET ANSI_NULLS ON GO     ALTER    procedure sp_script        @objectname varchar(50)asdeclare @databasename varchar(50) set @datab

SQL Server 查询分析器快捷键集合

下表列出 SQL Server 查询分析器提供的所有键盘快捷方式. 活动 快捷方式 书签:清除所有书签. CTRL-SHIFT-F2 书签:插入或删除书签(切换). CTRL+F2 书签:移动到下一个书签. F2 功能键 书签:移动到上一个书签. SHIFT+F2 取消查询. ALT+BREAK 连接:连接. CTRL+O 连接:断开连接. CTRL+F4 连接:断开连接并关闭子窗口. CTRL+F4 数据库对象信息. ALT+F1 编辑:清除活动的编辑器窗格. CTRL+SHIFT+DEL 编