Пример #1
0
        public static async Task <LuaObject> BinaryOperationAsync(Engine engine, BinaryOp op, LuaObject left,
                                                                  LuaObject right, CancellationToken token = default)
        {
            // Metatables
            var metaKey     = op.ToMetaKey();
            var isDifferent = op == BinaryOp.Different;

            if (isDifferent)
            {
                metaKey = BinaryOp.Equal.ToMetaKey();
            }

            if (!string.IsNullOrEmpty(metaKey) || isDifferent)
            {
                var meta = left.GetMetaMethod(engine, metaKey);

                if (meta.IsNil())
                {
                    meta = right.GetMetaMethod(engine, metaKey);
                }

                if (!meta.IsNil())
                {
                    var result = await meta.CallAsync(engine, Lua.Args(left, right), token).FirstAsync();

                    if (isDifferent)
                    {
                        result = !result.AsBool();
                    }

                    return(result);
                }
            }

            // Default implementations
            double leftDbl, rightDbl;

            switch (op)
            {
            // String
            case BinaryOp.Concat:
                return(left.AsString() + right.AsString());

            // Logic
            case BinaryOp.And:
                return(left.AsBool() ? right : left);

            case BinaryOp.Or:
                return(left.AsBool() ? left : right);

            // Relational
            case BinaryOp.Equal:
                return(left.Equals(right));

            case BinaryOp.Different:
                return(!left.Equals(right));

            case BinaryOp.LessThan:
            case BinaryOp.LessOrEqual:
            case BinaryOp.GreaterThan:
            case BinaryOp.GreaterOrEqual:
                if (left.IsString() && right.IsString())
                {
                    leftDbl  = StringComparer.Ordinal.Compare(left.AsString(), right.AsString());
                    rightDbl = 0;
                }
                else if (left.IsNumber() && right.IsNumber())
                {
                    leftDbl  = left.AsNumber();
                    rightDbl = right.AsNumber();
                }
                else
                {
                    throw new LuaException($"attempt to compare {right.Type.ToLuaName()} with {left.Type.ToLuaName()}");
                }

                break;

            // Arithmetic
            case BinaryOp.Addition:
            case BinaryOp.Division:
            case BinaryOp.Subtraction:
            case BinaryOp.Multiplication:
            case BinaryOp.Power:
            case BinaryOp.Modulo:
                leftDbl  = left.AsNumber();
                rightDbl = right.AsNumber();

                break;

            default:
                throw new ArgumentOutOfRangeException();
            }


            switch (op)
            {
            case BinaryOp.Addition:
                return(FromNumber(leftDbl + rightDbl));

            case BinaryOp.Division:
                return(FromNumber(leftDbl / rightDbl));

            case BinaryOp.Subtraction:
                return(FromNumber(leftDbl - rightDbl));

            case BinaryOp.Multiplication:
                return(FromNumber(leftDbl * rightDbl));

            case BinaryOp.Power:
                return(FromNumber(Math.Pow(leftDbl, rightDbl)));

            case BinaryOp.Modulo:
                return(FromNumber(leftDbl % rightDbl));

            case BinaryOp.LessThan:
                return(leftDbl < rightDbl);

            case BinaryOp.LessOrEqual:
                return(leftDbl <= rightDbl);

            case BinaryOp.GreaterThan:
                return(leftDbl > rightDbl);

            case BinaryOp.GreaterOrEqual:
                return(leftDbl >= rightDbl);

            default:
                throw new IndexOutOfRangeException();
            }
        }