一个精简的C#表达式执行框架Dynamic Expresso

一、简介

Dynamic Expresso是一个用.NET Standard 2.0编写的简单c#语句的解释器。Dynamic Expresso嵌入了自己的解析逻辑,通过将其转换为.NET lambda表达式或委托来解释c#语句。

使用Dynamic Expresso开发人员可以创建可编写脚本的应用程序,无需编译即可执行.NET代码,或者创建动态linq语句。

语句是使用c#语言规范的子集编写的。全局变量或参数可以被注入并在表达式中使用。它不会生成程序集,但会动态地创建表达式树。

二、安装

新建控制台项目DynamicExpressoResearch,并通过NUGet添加DynamicExpresso.Core;

三、功能和特性

  1. 返回值

    你可以解析并执行一个void类型没有返回值的表达式,或者也可以返回任何有效的.NET类型。我们可以在解析表达式的时候指定期望的返回类型。

static void EvalVoidExp()
        {
            var target = new Interpreter();
            var t = target.Eval("Program.empty()", new Parameter("Program", typeof(Program), new Program()));
            if (t == null)
            {
                Console.WriteLine("EvalVoidExp return value is null");
            }
        }
        
        //EvalVoidExp return value is null
static void EvalSpecReturnTypeExp()
        {
            var target = new Interpreter();
            double result = target.Eval<double>("Math.Pow(x, y) + 5",
                                new Parameter("x", typeof(double), 10),
                                new Parameter("y", typeof(double), 2));

            Console.WriteLine(string.Format("We specify the return value type of the expression as double, return value is {0}, value type is {1}", result, result.GetType().FullName));

            int r = target.Eval<int>("Math.Pow(x, y) + 5",
                                new Parameter("x", typeof(int), 10),
                                new Parameter("y", typeof(int), 2));
            Console.WriteLine(string.Format("We specify the return value type of the expression as int, return value is {0}, value type is {1}", r, r.GetType().FullName));
        }
        
        
        //We specify the return value type of the expression as double, return value is 105, value type is System.Double

        //We specify the return value type of the expression as int, return value is 105, value type is System.Int32
同时内置的表达式parser也可以自动感知表达式的返回类型;
static void AutoInferDataType()
        {
            var target = new Interpreter();
            object r = target.Eval("Math.Pow(x, y) + 5",
                                new Parameter("x", typeof(int), 10),
                                new Parameter("y", typeof(int), 2));
            Console.WriteLine(string.Format("We do not  specify the return value type of the expression, return value is {0}, value type is {1}", r, r.GetType().FullName));
        }

2.变量(Variables)

变量依附在Interpreter上,相当于一个全局的控制参数,可以应用到同一个Interpreter实例的所有表达式中;
Interpreter提供了不同的方法可以实现传入不同类型的变量;
SetVariable可以内置的原始类型及复杂的自定义数据类型;
static void SetParameter()
        {
            var target = new Interpreter();
            target.SetVariable("rate", 0.8);
            object r = target.Eval("price * rate",
                                new Parameter("price", typeof(int), 200));
            Console.WriteLine(string.Format("The price of 200 with a 20% discount is {0}", r));

            r = target.Eval("price * rate",
                                new Parameter("price", typeof(int), 499));
            Console.WriteLine(string.Format("The price of 499 with a 20% discount is {0}", r));
        }
        
        The price of 200 with a 20% discount is 160
        The price of 499 with a 20% discount is 399.2
SetFunction可以通过委托的方式传递函数
static void SetFunction()
        {
            var target = new Interpreter();
            Func<double, double, double> pow = (x, y) => Math.Pow(x, y);
            target.SetFunction("pow", pow);
            object r = target.Eval("pow(x, y)",
                                new Parameter("x", typeof(int), 10),
                                new Parameter("y", typeof(int), 2));
            Console.WriteLine(string.Format("10 to the second power  is {0}", r));

            r = target.Eval("pow(x, y)",
                                new Parameter("x", typeof(int), 2),
                                new Parameter("y", typeof(int), 4));
            Console.WriteLine(string.Format("2 to the fourth power is {0}", r));
        }
        
        //10 to the second power  is 100
        //2 to the fourth power is 16
SetExpression可以设置自定义的Expression
static void SetExpression()
        {
            var target = new Interpreter();
            var rateExp = Expression.Constant(0.8);
            target.SetExpression("rate", rateExp);
            object r = target.Eval("price * rate",
                                new Parameter("price", typeof(int), 200));
            Console.WriteLine(string.Format("The price of 200 with a 20% discount is {0}", r));

            r = target.Eval("price * rate",
                                new Parameter("price", typeof(int), 499));
            Console.WriteLine(string.Format("The price of 499 with a 20% discount is {0}", r));
        }

3.参数(Parameters)

参数需要每次执行的时候传递,参数可以是任意的类型,我们可以只解析一次表达式,并通过不同的参数进行多次调用;
static void Invoke()
        {
            var target = new Interpreter();

            var parameters = new[] {
                 new Parameter("x", typeof(int)),
                 new Parameter("y", typeof(int))
            };

            var myFunc = target.Parse("x + y", parameters);
            myFunc.Invoke(23, 7);
            myFunc.Invoke(32, -2);            
        }

4.内置类型和自定义类型

默认内部直接支持的数据类型有
Object object 
    Boolean bool 
    Char char
    String string
    SByte Byte byte
    Int16 UInt16 Int32 int UInt32 Int64 long UInt64 
    Single Double double Decimal decimal 
    DateTime TimeSpan
    Guid
    Math Convert
我们可以直接使用Interpreter.Reference来引用任何的.net类型
static void ReferenceType()
        {
            var target = new Interpreter().Reference(typeof(Uri));
            Console.WriteLine((target.Eval("typeof(Uri)") as Type).FullName);
            Console.WriteLine(target.Eval("Uri.UriSchemeHttp"));
        }
        
        //System.Uri
        //http

5.生成动态委托

我们可以直接使用Interpreter.ParseAsDelegate<TDelegate>来解析表达式生成对应的委托,然后可以直接调用对应的委托。
class Customer
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public char Gender { get; set; }
        }

        static void DynamicDelegate()
        {
            var customers = new List<Customer> {
                new Customer() { Name = "David", Age = 31, Gender = 'M' },
                new Customer() { Name = "Mary", Age = 29, Gender = 'F' },
                new Customer() { Name = "Jack", Age = 2, Gender = 'M' },
                new Customer() { Name = "Marta", Age = 1, Gender = 'F' },
                new Customer() { Name = "Moses", Age = 120, Gender = 'M' },
            };

            string whereExpression = "customer.Age > 18 && customer.Gender == 'F' && index >=0";

            var interpreter = new Interpreter();
            Func<Customer, int, bool> dynamicWhere = interpreter.ParseAsDelegate<Func<Customer, int, bool>>(whereExpression, "customer", "index");
            Console.WriteLine(customers.Where(dynamicWhere).Count());
        }

6.生成lambda表达式

我们可以使用Interpreter.ParseAsExpression<TDelegate>解释表达式直接生成lambda表达式;
static void DynamicLambdaExpress()
        {
            var customers = new List<Customer> {
                new Customer() { Name = "David", Age = 31, Gender = 'M' },
                new Customer() { Name = "Mary", Age = 29, Gender = 'F' },
                new Customer() { Name = "Jack", Age = 2, Gender = 'M' },
                new Customer() { Name = "Marta", Age = 1, Gender = 'F' },
                new Customer() { Name = "Moses", Age = 120, Gender = 'M' },
            }.AsQueryable();

            string whereExpression = "customer.Age > 18 && customer.Gender == 'F' && index >=0";

            var interpreter = new Interpreter();
            Expression<Func<Customer, int, bool>> dynamicWhere = interpreter.ParseAsExpression<Func<Customer, int, bool>>(whereExpression, "customer", "index");
            Console.WriteLine(customers.Where(dynamicWhere).Count());
        }

7.支持的操作符

CategoryOperators
Primaryx.y f(x) a[x] new typeof
Unary+ - ! (T)x
Multiplicative* / %
Additive+ -
Relational and type testing< > <= >= is as
Equality== !=
Logical AND&
Logical OR
Logical XOR^
Conditional AND&&
Conditional OR
Conditional?:
Assignment=
Null coalescing??

8.文本标识

CategoryOperators
Constantstrue false null
Real literal suffixesd f m
Integer literal suffixesu l ul lu
String/char"" ''

字符串或者字符类型中支持的转译

  • ' - single quote, needed for character literals

  • " - double quote, needed for string literals

  • \ - backslash

  • \0 - Unicode character 0

  • \a - Alert (character 7)

  • \b - Backspace (character 8)

  • \f - Form feed (character 12)

  • \n - New line (character 10)

  • \r - Carriage return (character 13)

  • \t - Horizontal tab (character 9)

  • \v - Vertical quote (character 11)

9.调用对象成员

可以在表达式中直接调用对象实例的成员
public class Student
        {
            public string Name { get; set; }
            public int Age { get; set; }

            public void Hello()
            {
                Console.WriteLine(string.Format("hello, my name is {0}, my age is {1}", Name, Age));
            }

            public static Student Instance()
            {
                return new Student() { Name="auto", Age = 0};
            }
        }

        static void InvokTypeMember()
        {
            var s = new Student() { Name="mango", Age = 30};
            var target = new Interpreter().SetVariable("s", s);
            target.Reference(typeof(Student));
            Console.WriteLine(target.Eval("s.Hello()"));
            Console.WriteLine(target.Eval("s.Name"));
            Console.WriteLine(target.Eval("new Student().Hello()"));
            Console.WriteLine(target.Eval("Student.Instance().Hello()"));
        }
同时可以支持扩展方法的调用;
支持数组索引的调用;
支持params数组;
部分支持泛型;
static void InvokCollectionMember()
        {
            var x = new int[] { 10, 30, 4 };          
            var target = new Interpreter();
            target.SetVariable("x", x);
            Console.WriteLine(target.Eval("x.Count()"));
            //Console.WriteLine(target.Eval("x.Count<int>()"));
            Console.WriteLine(target.Eval("x[0]"));
            var s = new string[] {"mango", "is","test","params" };
            target.SetVariable("s", s);
            Console.WriteLine(target.Eval("string.Join(\" \", s)"));
        }

10.Lambda表达式

Dynamic Expresso只支持lambda表达式的部分功能;为了减少对性能的影响,默认是不开启Lambda表达式的解析的;我们可以通过InterpreterOptions.LambdaExpressions 来启用这个功能;
static void EvalAsLambda()
        {
            var x = new string[] { "this", "is", "awesome" };
            var options = InterpreterOptions.Default | InterpreterOptions.LambdaExpressions; // enable lambda expressions
            var target = new Interpreter(options)
                .SetVariable("x", x);

            var results = target.Eval<IEnumerable<string>>("x.Where(str => str.Length > 5).Select(str => str.ToUpper())");
            Console.WriteLine(results.First());
        }

11.大小写

默认情况下表达式是区分大小写的,可以通过InterpreterOptions.CaseInsensitive或者InterpreterOptions.DefaultCaseInsensitive来忽略大小写;
static void IgnorCase()
        {
            var target = new Interpreter(InterpreterOptions.CaseInsensitive);

            double x = 2;
            var parameters = new[] {
                new Parameter("x", x.GetType(), x)
            };

            Console.WriteLine(target.Eval("x", parameters));
            Console.WriteLine(target.Eval("X", parameters));
        }

12.标识符探测

Dynamic Expresso支持使用Interpreter.DetectIdentifiers来获取表达式中的变量标识符。
static void DetectIdentifier()
        {
            var target = new Interpreter();
            var detectedIdentifiers = target.DetectIdentifiers("x + y");

            Console.WriteLine(detectedIdentifiers.UnknownIdentifiers.First());
            Console.WriteLine(detectedIdentifiers.UnknownIdentifiers.Last());
        }

13.默认数字类型

默认情况下数字会被解析成int类型或者double类型;但是在有些情况下,例如财务中需要使用decimal类型的数字;我们可以使用Interpreter.SetDefaultNumberType来设置;
static void SetNumberType()
        {
            var target = new Interpreter();
            target.SetDefaultNumberType(DefaultNumberType.Decimal);
            Console.WriteLine(target.Eval("45").GetType().FullName);
            Console.WriteLine(target.Eval("10/3"));
        }

14.异常

提供了ParseException作为解析表达式异常的基类,同时提供了数个不同的具体的异常类,例如UnknownIdentifierException, NoApplicableMethodException;

15.多线程

Interpreter类可以在多线程中使用,但是需要确保在多线程中只能调用get属性和Parse及Eval这些方法,而对于初始化的一些设置(SetVariable/Reference)等只能在实例化对象的阶段调用;

如果我们需要使用不同的参数多次执行表达式,最好的方式就是解析一次表达式,并调用多次解析的结果;

16.安全性

表达式中只能访问Reference中设置的类型以及设置的变量和参数的对象实例;我们需要慎重考虑暴露哪些类给用户;

从1.3版本其默认禁用直接调用除了Type.Name的反射类方法;

17.功能局限

Not every C# syntaxes are supported. Here some examples of NOT supported features:

  • Multiline expressions

  • for/foreach/while/do operators

  • Array/list/dictionary initialization

  • Explicit generic invocation (like method

    (arg))
  • Lambda/delegate declaration (delegate and lamda are only supported as variables or parameters or as a return type of the expression)

  • Array/list/dictionary element assignment (set indexer operator)

  • Other operations on dynamic objects (only property, method invocation and index now are supported)

18.使用场景

  • Programmable applications

  • Allow the user to inject customizable rules and logic without recompiling

  • Evaluate dynamic functions or commands

  • LINQ dynamic query

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 精简指令集(Reduced Instruction Set Computing,简称RISC)CPU是一种基于精简指令集的中央处理器设计。相较于传统的复杂指令集(Complex Instruction Set Computing,简称CISC)CPU,RISC CPU采用了更加简化和精简的指令集架构,以提高CPU运算效率和降低芯片设计与制造的成本。 设计一个精简指令集CPU需要考虑以下几个方面: 首先,需要确定CPU的基本架构和功能。包括寄存器的数量和位宽、数据通路的设计、指令格式等。可以选择具有高性能、低功耗的结构,并确定适合目标应用的特殊功能模块,如浮点运算单元或向量处理单元。 其次,需要设计指令集的指令类型和操作数格式。指令类型通常包括算术逻辑运算、数据传输、条件分支和跳转等。操作数格式确定了指令的位宽和寻址方式,可以采用统一的格式简化指令处理逻辑。 接下来,需要定义每个指令的具体功能和执行流程。每个指令的操作码和操作数位域需要明确定义,以及指令执行的时序和数据通路。通过优化指令的执行顺序和并行度,提高CPU的运算效率。 在设计过程中,还需要考虑指令的编码方式和指令流水线的设计。指令的编码方式要尽量简洁和高效,以减少指令内存的占用。指令流水线的设计要充分发挥指令级并行和流水线级并行的优势,以提高CPU的吞吐量和执行效率。 最后,需要进行仿真和验证,确保设计的CPU能够正确地执行指令并满足预期的功能和性能要求。在验证过程中,可以利用仿真工具和测试套件对CPU进行功能验证和性能测试,发现和修复设计中可能存在的问题。 综上所述,设计一个精简指令集CPU需要从架构和功能、指令类型和操作数格式、指令功能和执行流程、指令编码和流水线设计等多个方面进行综合考虑,以提高CPU运算效率和降低芯片设计与制造的成本。 ### 回答2: 精简指令集CPU是一种通过减少指令数量和复杂性来提高执行效率和性能的中央处理器设计。下面将详细介绍如何设计一个精简指令集CPU。 首先,需要选择适当的指令集架构。精简指令集CPU常用的架构包括RISC(精简指令集计算机)和ARM(先进的精简指令集计算机)。选择适当的指令集架构可以在保持高性能的同时减少电路复杂性。 接下来,设计指令寄存器。指令寄存器用于存储当前正在执行的指令,可以通过指令的操作码(opcode)和操作数(operand)来解码指令。可以使用一个单独的寄存器或多个寄存器来实现指令寄存器。 然后,设计运算单元。运算单元用于执行算术和逻辑操作,包括加法、减法、乘法、除法、位操作等。可以使用多个模块来实现不同的运算操作,然后通过总线进行数据传输。 此外,还需要设计一个存储器单元。存储器单元用于存储指令和数据。可以使用分立的指令存储器和数据存储器,也可以使用统一的存储器来存储指令和数据。 最后,设计控制单元。控制单元用于控制指令的执行顺序和流程,包括指令的取指、解码、执行和写回。可以使用有限状态机(finite-state machine)来实现控制单元。 设计完毕后,可以通过制作原型电路板来测试并优化CPU的性能。通过模拟和仿真,可以验证电路设计的正确性和稳定性,并进行性能调整和优化。 总而言之,设计精简指令集CPU需要考虑指令集架构的选择、指令寄存器、运算单元、存储器单元和控制单元的设计。通过适当的设计和优化,可以实现高性能、低功耗的精简指令集CPU。 ### 回答3: 精简指令集(cpu)是一种设计简单但功能齐全的处理器。它的主要目的是降低设计复杂度,提高性能和功耗效率。下面是一个关于如何设计一个精简指令集cpu的简单介绍。 首先,精简指令集cpu的设计应该从需求和目标开始。确定需要的功能和性能,并了解电路设计的约束条件。 接下来,确定需要的指令集。精简指令集应该包含最基本的操作,如加法、减法、乘法、除法和逻辑运算。另外,还可以添加一些常用的指令,如加载数据、存储数据和跳转等。 然后,根据指令集的需求设计指令编码和操作码。指令编码是指给每个指令分配独特的二进制编码。操作码表示每个指令的具体操作和功能。 接着,设计ALU(算术逻辑单元)和寄存器等核心组件。ALU负责执行算术和逻辑运算,而寄存器用于存储和操作指令和数据。 之后,设计数据通路和控制单元。数据通路负责指令和数据的传输以及ALU的操作。控制单元则负责控制整个处理器的操作流程。 最后,进行仿真和测试,确保处理器的设计能够正确执行指令和达到预期的性能指标。进行必要的优化和调整。 总的来说,设计一个精简指令集cpu需要明确需求和目标,选择适当的指令集,设计合适的指令编码和操作码,设计核心组件和控制单元,并进行仿真和测试。这样可以保证处理器的设计满足功能需求,并具备较好的性能和功耗效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值