对于我的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)); } } }