#include <stdio.h>
#include <math.h>

typedef enum { CONSTANT, VARIABLE, OPERATOR } ExpressionType;

typedef struct {
	ExpressionType expressionType;
	void* expression;
} Expression;

typedef struct {} Variable;

typedef struct {
	float value;
} Constant;

typedef enum { PLUS, MINUS, MULTIPLY, DIVIDE } OperatorType;

typedef struct {
	OperatorType operatorType;
	Expression a;
	Expression b;
} Operator;

typedef struct {
	Variable* variable;
	float value;
} Binding;

typedef struct {
	Binding* bindings;
	size_t size;
} Context;

float value(Variable* variable, Context context) {
	for (size_t i = 0; i < context.size; i++) {
		if (context.bindings[i].variable == variable) {
			return context.bindings[i].value;
		}
	}

	return NAN;
}

float result(Expression expression, Context context);

float resultOfOperator(Operator* op, Context context) {
	float aResult = result(op->a, context);
	float bResult = result(op->b, context);
	
	switch (op->operatorType) {
	case PLUS:
		return aResult + bResult;

	case MINUS:
		return aResult - bResult;

	case MULTIPLY:
		return aResult * bResult;

	case DIVIDE:
		return aResult / bResult;
	}
	
	return NAN;
}

float result(Expression expression, Context context) {
	switch (expression.expressionType) {
	case CONSTANT:
		return *(float*)expression.expression;
	
	case VARIABLE:
		return value((Variable*)expression.expression, context);

	case OPERATOR:
		return resultOfOperator((Operator*)expression.expression, context);
	}
	
	return NAN;
}

int main(void) {
	Variable a;
	Variable b;
	Variable c;
	Variable d;

	Constant five = { 5.0 };
	Operator divide = { DIVIDE, { VARIABLE, &a }, { VARIABLE, &b } };
	Operator multiply = { MULTIPLY, { OPERATOR, &divide }, { VARIABLE, &c } };
	Operator minus = { MINUS, { OPERATOR, &multiply }, { VARIABLE, &d } };
	Operator plus = { PLUS, { OPERATOR, &minus }, { CONSTANT, &five } };

	Expression expression = { OPERATOR, &plus };

	Binding bindings[] = { { &a, 1.0 }, { &b, 2.0 }, { &c, 3.0 }, { &d, 4.0 } };
	Context context = { bindings, sizeof bindings };
	printf("%f", result(expression, context));

	return 0;
}
