在C#中使用运算符时构造自定义表达式树

前端之家收集整理的这篇文章主要介绍了在C#中使用运算符时构造自定义表达式树前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
这个问题是使用C#(或任何其他语言)中的运算符在.NET中构造自定义表达式树.我提供了一些背景信息.

对于我的managed 2-phase 64-bit assembler,我需要表达式的支持.例如,人们可能想要组装:

mystring: DB 'hello,world'
          TIMES 64-$+mystring DB ' '

表达式64- $mystring不能是一个字符串,而是一个实际的有效表达式,具有语法和类型检查以及VS中的IntelliSense功能,其特点如下:

64 - Reference.CurrentOffset + new Reference("mystring");

构建此表达式时不进行评估.相反,它将在我的汇编器的上下文中(当它确定符号偏移量等等时)进行评估. .NET框架(自.NET 3.5以来)提供了对表达式树的支持,在我看来,它对于稍后或其他地方进行评估的这种表达式是理想的.

但是我不知道如何确保我可以使用C#语法(使用<<%等等)来构造表达式树.我想防止像:

var expression = AssemblerExpression.Subtract(64,AssemblerExpression.Add(AssemblerExpression.CurrentOffset(),AssemblerExpression.Reference("mystring")))

你会怎么样呢?

注意:我需要一个表达式树,以便将表达式转换为可接受的自定义字符串表示形式,同时可以在不同于其定义的时间点对其进行评估.

对我的例子的解释:64- $mystring. $是当前的偏移量,因此它是一个预先未知的特定数字(但在评估时已知).哨子是在评估时间可能或可能不知道的符号(例如,当尚未被定义时).从符号S中减去常数C与S-C相同.减去两个符号S0和S1(S1 – S0)给出两个符号值之间的整数差.

但是,这个问题并不在于如何评估汇编器表达式,而是更多地关于如何评估其中有自定义类的任何表达式(例如符号和示例中的$)以及如何确保它可以是漂亮的 – 使用一些访客(因此保留树)打印.而且由于.NET框架具有表达式的树和访问者,所以如果可能的话,使用它们是很好的.

解决方法

我不知道你正在瞄准什么,但以下是我认为可以工作的一些粗略的方法.

注意我

>仅演示索引的引用表达式(因此忽略了通过寄存器的间接寻址现在;您可以添加类似于SymbolicReference类的RegisterInderectReference).这也适用于您建议的$(当前偏移)功能.它可能会确定一个注册表(?)
>没有明确地显示一元/二进制运算符.然而,力学基本相同.我没有添加它,因为我无法解决您的问题中的示例表达式的语义(我认为减去一个已知字符串的地址是没有用的,例如)
>方法不放置(语义)限制:您可以偏移任何ReferenceBase派生的IReference.实际上,您可能只想允许一个级别的索引,并且直接在SymbolicReference上定义运算符将更为合适.
>为了演示目的牺牲了编码风格(一般来说,你不想反复编译你的表达式树,并且直接用.Compile()()进行评估看起来很丑陋,令人困惑,它由OP把它整合起来以更清晰的方式
>显式转换运算符的演示真的是非主题.我被吓倒了(?)
>您可以观察代码running live on IdeOne.com

.

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Linq;


namespace Assembler
{
    internal class State
    {
        public readonly IDictionary<string,ulong> SymbolTable = new Dictionary<string,ulong>();

        public void Clear() 
        {
            SymbolTable.Clear();
        }
    }

    internal interface IReference
    {
        ulong EvalAddress(State s); // evaluate reference to address
    }

    internal abstract class ReferenceBase : IReference
    {
        public static IndexedReference operator+(long directOffset,ReferenceBase baseRef) { return new IndexedReference(baseRef,directOffset); }
        public static IndexedReference operator+(ReferenceBase baseRef,long directOffset) { return new IndexedReference(baseRef,directOffset); }

        public abstract ulong EvalAddress(State s);
    }

    internal class SymbolicReference : ReferenceBase
    {
        public static explicit operator SymbolicReference(string symbol)    { return new SymbolicReference(symbol); }
        public SymbolicReference(string symbol) { _symbol = symbol; }

        private readonly string _symbol;

        public override ulong EvalAddress(State s) 
        {
            return s.SymbolTable[_symbol];
        }

        public override string ToString() { return string.Format("Sym({0})",_symbol); }
    }

    internal class IndexedReference : ReferenceBase
    {
        public IndexedReference(IReference baseRef,long directOffset) 
        {
            _baseRef = baseRef;
            _directOffset = directOffset;
        }

        private readonly IReference _baseRef;
        private readonly long _directOffset;

        public override ulong EvalAddress(State s) 
        {
            return (_directOffset<0)
                ? _baseRef.EvalAddress(s) - (ulong) Math.Abs(_directOffset)
                : _baseRef.EvalAddress(s) + (ulong) Math.Abs(_directOffset);
        }

        public override string ToString() { return string.Format("{0} + {1}",_directOffset,_baseRef); }
    }
}

namespace Program
{
    using Assembler;

    public static class Program
    {
        public static void Main(string[] args)
        {
            var myBaseRef1 = new SymbolicReference("mystring1");

            Expression<Func<IReference>> anyRefExpr = () => 64 + myBaseRef1;
            Console.WriteLine(anyRefExpr);

            var myBaseRef2 = (SymbolicReference) "mystring2"; // uses explicit conversion operator

            Expression<Func<IndexedReference>> indexedRefExpr = () => 64 + myBaseRef2;
            Console.WriteLine(indexedRefExpr);

            Console.WriteLine(Console.Out.NewLine + "=== show compiletime types of returned values:");
            Console.WriteLine("myBaseRef1     -> {0}",myBaseRef1);
            Console.WriteLine("myBaseRef2     -> {0}",myBaseRef2);
            Console.WriteLine("anyRefExpr     -> {0}",anyRefExpr.Compile().Method.ReturnType);
            Console.WriteLine("indexedRefExpr -> {0}",indexedRefExpr.Compile().Method.ReturnType);

            Console.WriteLine(Console.Out.NewLine + "=== show runtime types of returned values:");
            Console.WriteLine("myBaseRef1     -> {0}",anyRefExpr.Compile()());     // compile() returns Func<...>
            Console.WriteLine("indexedRefExpr -> {0}",indexedRefExpr.Compile()());

            Console.WriteLine(Console.Out.NewLine + "=== observe how you could add an evaluation model using some kind of symbol table:");
            var compilerState = new State();
            compilerState.SymbolTable.Add("mystring1",0xdeadbeef); // raw addresses
            compilerState.SymbolTable.Add("mystring2",0xFeedface);

            Console.WriteLine("myBaseRef1 evaluates to     0x{0:x8}",myBaseRef1.EvalAddress(compilerState));
            Console.WriteLine("myBaseRef2 evaluates to     0x{0:x8}",myBaseRef2.EvalAddress(compilerState));
            Console.WriteLine("anyRefExpr displays as      {0:x8}",anyRefExpr.Compile()());
            Console.WriteLine("indexedRefExpr displays as  {0:x8}",indexedRefExpr.Compile()());
            Console.WriteLine("anyRefExpr evaluates to     0x{0:x8}",anyRefExpr.Compile()().EvalAddress(compilerState));
            Console.WriteLine("indexedRefExpr evaluates to 0x{0:x8}",indexedRefExpr.Compile()().EvalAddress(compilerState));
        }
    }
}

猜你在找的C#相关文章