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(); } }