Flee
This commit is contained in:
348
ExpressionElements/LogicalBitwise/AndOr.cs
Normal file
348
ExpressionElements/LogicalBitwise/AndOr.cs
Normal file
@@ -0,0 +1,348 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.LogicalBitwise
|
||||
{
|
||||
internal class AndOrElement : BinaryExpressionElement
|
||||
{
|
||||
private AndOrOperation _myOperation;
|
||||
private static readonly object OurTrueTerminalKey = new object();
|
||||
private static readonly object OurFalseTerminalKey = new object();
|
||||
private static readonly object OurEndLabelKey = new object();
|
||||
|
||||
public void New()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
_myOperation = (AndOrOperation)operation;
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
Type bitwiseOpType = Utility.GetBitwiseOpType(leftType, rightType);
|
||||
if ((bitwiseOpType != null))
|
||||
{
|
||||
return bitwiseOpType;
|
||||
}
|
||||
else if (this.AreBothChildrenOfType(typeof(bool)))
|
||||
{
|
||||
return typeof(bool);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Type resultType = this.ResultType;
|
||||
|
||||
if (object.ReferenceEquals(resultType, typeof(bool)))
|
||||
{
|
||||
this.DoEmitLogical(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, resultType, ilg);
|
||||
MyRightChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, resultType, ilg);
|
||||
EmitBitwiseOperation(ilg, _myOperation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBitwiseOperation(FleeILGenerator ilg, AndOrOperation op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case AndOrOperation.And:
|
||||
ilg.Emit(OpCodes.And);
|
||||
break;
|
||||
case AndOrOperation.Or:
|
||||
ilg.Emit(OpCodes.Or);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail("Unknown op type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DoEmitLogical(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// We have to do a 'fake' emit so we can get the positions of the labels
|
||||
ShortCircuitInfo info = new ShortCircuitInfo();
|
||||
|
||||
// Do the real emit
|
||||
this.EmitLogical(ilg, info, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a short-circuited logical operation sequence
|
||||
/// The idea: Store all the leaf operands in a stack with the leftmost at the top and rightmost at the bottom.
|
||||
/// For each operand, emit it and try to find an end point for when it short-circuits. This means we go up through
|
||||
/// the stack of operators (ignoring siblings) until we find a different operation (then emit a branch to its right operand)
|
||||
/// or we reach the root (emit a branch to a true/false).
|
||||
/// Repeat the process for all operands and then emit the true/false/last operand end cases.
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="services"></param>
|
||||
private void EmitLogical(FleeILGenerator ilg, ShortCircuitInfo info, IServiceProvider services)
|
||||
{
|
||||
// We always have an end label
|
||||
Label endLabel = ilg.DefineLabel();
|
||||
|
||||
// Populate our data structures
|
||||
this.PopulateData(info);
|
||||
|
||||
// Emit the sequence
|
||||
EmitLogicalShortCircuit(ilg, info, services);
|
||||
|
||||
// Get the last operand
|
||||
ExpressionElement terminalOperand = (ExpressionElement)info.Operands.Pop();
|
||||
// Emit it
|
||||
EmitOperand(terminalOperand, info, ilg, services);
|
||||
|
||||
// only 1-3 opcodes, always a short branch
|
||||
ilg.EmitBranch(endLabel);
|
||||
|
||||
// Emit our true/false terminals
|
||||
EmitTerminals(info, ilg, endLabel);
|
||||
|
||||
// Mark the end
|
||||
ilg.MarkLabel(endLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a sequence of and/or expressions with short-circuiting
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitLogicalShortCircuit(FleeILGenerator ilg, ShortCircuitInfo info, IServiceProvider services)
|
||||
{
|
||||
while (info.Operators.Count != 0)
|
||||
{
|
||||
// Get the operator
|
||||
AndOrElement op = (AndOrElement)info.Operators.Pop();
|
||||
// Get the left operand
|
||||
ExpressionElement leftOperand = (ExpressionElement)info.Operands.Pop();
|
||||
|
||||
// Emit the left
|
||||
EmitOperand(leftOperand, info, ilg, services);
|
||||
|
||||
// Get the label for the short-circuit case
|
||||
Label l = GetShortCircuitLabel(op, info, ilg);
|
||||
// Emit the branch
|
||||
EmitBranch(op, ilg, l, info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void EmitBranch(AndOrElement op, FleeILGenerator ilg, Label target, ShortCircuitInfo info)
|
||||
{
|
||||
// Get the branch opcode
|
||||
if (op._myOperation == AndOrOperation.And)
|
||||
ilg.EmitBranchFalse(target);
|
||||
else
|
||||
ilg.EmitBranchTrue(target);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the label for a short-circuit
|
||||
/// </summary>
|
||||
/// <param name="current"></param>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <returns></returns>
|
||||
private static Label GetShortCircuitLabel(AndOrElement current, ShortCircuitInfo info, FleeILGenerator ilg)
|
||||
{
|
||||
// We modify the given stacks so we need to clone them
|
||||
Stack cloneOperands = (Stack)info.Operands.Clone();
|
||||
Stack cloneOperators = (Stack)info.Operators.Clone();
|
||||
|
||||
// Pop all siblings
|
||||
current.PopRightChild(cloneOperands, cloneOperators);
|
||||
|
||||
// Go until we run out of operators
|
||||
while (cloneOperators.Count > 0)
|
||||
{
|
||||
// Get the top operator
|
||||
AndOrElement top = (AndOrElement)cloneOperators.Pop();
|
||||
|
||||
// Is is a different operation?
|
||||
if (top._myOperation != current._myOperation)
|
||||
{
|
||||
// Yes, so return a label to its right operand
|
||||
object nextOperand = cloneOperands.Pop();
|
||||
return GetLabel(nextOperand, ilg, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so keep going up the stack
|
||||
top.PopRightChild(cloneOperands, cloneOperators);
|
||||
}
|
||||
}
|
||||
|
||||
// We've reached the end of the stack so return the label for the appropriate true/false terminal
|
||||
if (current._myOperation == AndOrOperation.And)
|
||||
{
|
||||
return GetLabel(OurFalseTerminalKey, ilg, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetLabel(OurTrueTerminalKey, ilg, info);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopRightChild(Stack operands, Stack operators)
|
||||
{
|
||||
AndOrElement andOrChild = MyRightChild as AndOrElement;
|
||||
|
||||
// What kind of child do we have?
|
||||
if ((andOrChild != null))
|
||||
{
|
||||
// Another and/or expression so recurse
|
||||
andOrChild.Pop(operands, operators);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A terminal so pop it off the operands stack
|
||||
operands.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively pop operators and operands
|
||||
/// </summary>
|
||||
/// <param name="operands"></param>
|
||||
/// <param name="operators"></param>
|
||||
private void Pop(Stack operands, Stack operators)
|
||||
{
|
||||
operators.Pop();
|
||||
|
||||
AndOrElement andOrChild = MyLeftChild as AndOrElement;
|
||||
if (andOrChild == null)
|
||||
{
|
||||
operands.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
andOrChild.Pop(operands, operators);
|
||||
}
|
||||
|
||||
andOrChild = MyRightChild as AndOrElement;
|
||||
|
||||
if (andOrChild == null)
|
||||
{
|
||||
operands.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
andOrChild.Pop(operands, operators);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitOperand(ExpressionElement operand, ShortCircuitInfo info, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Is this operand the target of a label?
|
||||
if (info.HasLabel(operand) == true)
|
||||
{
|
||||
// Yes, so mark it
|
||||
Label leftLabel = info.FindLabel(operand);
|
||||
ilg.MarkLabel(leftLabel);
|
||||
}
|
||||
|
||||
// Emit the operand
|
||||
operand.Emit(ilg, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit the end cases for a short-circuit
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="endLabel"></param>
|
||||
private static void EmitTerminals(ShortCircuitInfo info, FleeILGenerator ilg, Label endLabel)
|
||||
{
|
||||
// Emit the false case if it was used
|
||||
if (info.HasLabel(OurFalseTerminalKey) == true)
|
||||
{
|
||||
Label falseLabel = info.FindLabel(OurFalseTerminalKey);
|
||||
|
||||
// Mark the label and note its position
|
||||
ilg.MarkLabel(falseLabel);
|
||||
|
||||
ilg.Emit(OpCodes.Ldc_I4_0);
|
||||
|
||||
// If we also have a true terminal, then skip over it
|
||||
if (info.HasLabel(OurTrueTerminalKey) == true)
|
||||
{
|
||||
// only 1-3 opcodes, always a short branch
|
||||
ilg.Emit(OpCodes.Br_S, endLabel);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the true case if it was used
|
||||
if (info.HasLabel(OurTrueTerminalKey) == true)
|
||||
{
|
||||
Label trueLabel = info.FindLabel(OurTrueTerminalKey);
|
||||
|
||||
// Mark the label and note its position
|
||||
ilg.MarkLabel(trueLabel);
|
||||
|
||||
ilg.Emit(OpCodes.Ldc_I4_1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Label GetLabel(object key, FleeILGenerator ilg, ShortCircuitInfo info)
|
||||
{
|
||||
if (info.HasLabel(key))
|
||||
return info.FindLabel(key);
|
||||
return info.AddLabel(key, ilg.DefineLabel());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visit the nodes of the tree (right then left) and populate some data structures
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
private void PopulateData(ShortCircuitInfo info)
|
||||
{
|
||||
// Is our right child a leaf or another And/Or expression?
|
||||
AndOrElement andOrChild = MyRightChild as AndOrElement;
|
||||
if (andOrChild == null)
|
||||
{
|
||||
// Leaf so push it on the stack
|
||||
info.Operands.Push(MyRightChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Another And/Or expression so recurse
|
||||
andOrChild.PopulateData(info);
|
||||
}
|
||||
|
||||
// Add ourselves as an operator
|
||||
info.Operators.Push(this);
|
||||
|
||||
// Do the same thing for the left child
|
||||
andOrChild = MyLeftChild as AndOrElement;
|
||||
|
||||
if (andOrChild == null)
|
||||
{
|
||||
info.Operands.Push(MyLeftChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
andOrChild.PopulateData(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
ExpressionElements/LogicalBitwise/Not.cs
Normal file
46
ExpressionElements/LogicalBitwise/Not.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.LogicalBitwise
|
||||
{
|
||||
internal class NotElement : UnaryElement
|
||||
{
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
if (object.ReferenceEquals(MyChild.ResultType, typeof(bool)))
|
||||
{
|
||||
this.EmitLogical(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyChild.Emit(ilg, services);
|
||||
ilg.Emit(OpCodes.Not);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLogical(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MyChild.Emit(ilg, services);
|
||||
ilg.Emit(OpCodes.Ldc_I4_0);
|
||||
ilg.Emit(OpCodes.Ceq);
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type childType)
|
||||
{
|
||||
if (object.ReferenceEquals(childType, typeof(bool)))
|
||||
{
|
||||
return typeof(bool);
|
||||
}
|
||||
else if (Utility.IsIntegralType(childType) == true)
|
||||
{
|
||||
return childType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
ExpressionElements/LogicalBitwise/Xor.cs
Normal file
44
ExpressionElements/LogicalBitwise/Xor.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.LogicalBitwise
|
||||
{
|
||||
internal class XorElement : BinaryExpressionElement
|
||||
{
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
Type bitwiseType = Utility.GetBitwiseOpType(leftType, rightType);
|
||||
|
||||
if ((bitwiseType != null))
|
||||
{
|
||||
return bitwiseType;
|
||||
}
|
||||
else if (this.AreBothChildrenOfType(typeof(bool)) == true)
|
||||
{
|
||||
return typeof(bool);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Type resultType = this.ResultType;
|
||||
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, resultType, ilg);
|
||||
MyRightChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, resultType, ilg);
|
||||
ilg.Emit(OpCodes.Xor);
|
||||
}
|
||||
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user