How to custom data structure and lower custom operaor?

Hi guys:

I have an custom container Array2D which is just like std::vector<std::vector>.
This container is not like tensor due to the size of every row are not quite the same.

template<typename T>
class Array2D {
public:
	const T& at(int i, int j) {
		return buf_[i][j];
	}
	const std::vector<T>& at(int i) {
		return buf_[i];
	}	
private:
    // I use google::RepeatedField<T> in producting system indeed.
    std::vector<std::vector<T>> buf_;
};

I also want to add some operator to calculator Array2D. such as an element-wise Add operator:

template<typename T>
class Add : public dag::Operator {
public:
	int compute(OperatorContext *ctx) {
		const Array2D<T>* a = ctx->get_input<Array2D<T>>(0);
		const Array2D<T>* b = ctx->get_input<Array2D<T>>(1);
		Array2D<T>* c = ctx->get_output<Array2D<T>>(0);
		for (int i = 0; i < a->size(); ++i) {
			auto &one_row = a->at(i);
			for (int j = 0; j < one_row.size(); ++j) {
				*c[i][j] = a->at(i).at(j) + b->at(i).at(j);
			}
		}
        return 0;
	}
};

To make such operator more conveniently, I want provide a DSL such as:

a = [[1,2],[3,4,5,6]]
b = [[8,9],[4,3,2,1]]
c = ops.Add(a, b) # result: [[9,11],[7,7,7,7]]

So, To build such system, How do i extend the Array2D to MLIR and lower the Add operator?