Step 6: Implement evaluator with TDD

- Eval(ast.Node) (float64, error) - recursive AST walker
- Handles all 4 operators: +, -, *, /
- Division by zero returns error
- Error propagation from sub-expressions
- 10 unit tests covering: single number, all operators, nested/deep
  expressions, division by zero (direct and in sub-expr), floats
diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go
new file mode 100644
index 0000000..447adbe
--- /dev/null
+++ b/evaluator/evaluator.go
@@ -0,0 +1,45 @@
+package evaluator
+
+import (
+	"fmt"
+	"matheval/ast"
+	"matheval/token"
+)
+
+// Eval evaluates an AST node and returns the result.
+// Returns an error on division by zero.
+func Eval(node ast.Node) (float64, error) {
+	switch n := node.(type) {
+	case *ast.NumberLit:
+		return n.Value, nil
+
+	case *ast.BinaryExpr:
+		left, err := Eval(n.Left)
+		if err != nil {
+			return 0, err
+		}
+		right, err := Eval(n.Right)
+		if err != nil {
+			return 0, err
+		}
+
+		switch n.Op {
+		case token.Plus:
+			return left + right, nil
+		case token.Minus:
+			return left - right, nil
+		case token.Star:
+			return left * right, nil
+		case token.Slash:
+			if right == 0 {
+				return 0, fmt.Errorf("division by zero")
+			}
+			return left / right, nil
+		default:
+			return 0, fmt.Errorf("unknown operator: %v", n.Op)
+		}
+
+	default:
+		return 0, fmt.Errorf("unknown node type: %T", node)
+	}
+}
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
new file mode 100644
index 0000000..411463c
--- /dev/null
+++ b/evaluator/evaluator_test.go
@@ -0,0 +1,175 @@
+package evaluator
+
+import (
+	"math"
+	"matheval/ast"
+	"matheval/token"
+	"testing"
+)
+
+func TestEvalNumberLit(t *testing.T) {
+	result, err := Eval(&ast.NumberLit{Value: 42.5})
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 42.5 {
+		t.Fatalf("expected 42.5, got %v", result)
+	}
+}
+
+func TestEvalAddition(t *testing.T) {
+	node := &ast.BinaryExpr{
+		Op:    token.Plus,
+		Left:  &ast.NumberLit{Value: 1},
+		Right: &ast.NumberLit{Value: 2},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 3 {
+		t.Fatalf("expected 3, got %v", result)
+	}
+}
+
+func TestEvalSubtraction(t *testing.T) {
+	node := &ast.BinaryExpr{
+		Op:    token.Minus,
+		Left:  &ast.NumberLit{Value: 10},
+		Right: &ast.NumberLit{Value: 4},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 6 {
+		t.Fatalf("expected 6, got %v", result)
+	}
+}
+
+func TestEvalMultiplication(t *testing.T) {
+	node := &ast.BinaryExpr{
+		Op:    token.Star,
+		Left:  &ast.NumberLit{Value: 3},
+		Right: &ast.NumberLit{Value: 7},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 21 {
+		t.Fatalf("expected 21, got %v", result)
+	}
+}
+
+func TestEvalDivision(t *testing.T) {
+	node := &ast.BinaryExpr{
+		Op:    token.Slash,
+		Left:  &ast.NumberLit{Value: 10},
+		Right: &ast.NumberLit{Value: 4},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 2.5 {
+		t.Fatalf("expected 2.5, got %v", result)
+	}
+}
+
+func TestEvalDivisionByZero(t *testing.T) {
+	node := &ast.BinaryExpr{
+		Op:    token.Slash,
+		Left:  &ast.NumberLit{Value: 5},
+		Right: &ast.NumberLit{Value: 0},
+	}
+	_, err := Eval(node)
+	if err == nil {
+		t.Fatal("expected division by zero error")
+	}
+}
+
+func TestEvalNestedExpr(t *testing.T) {
+	// (1 + 2) * (8 / 4) = 3 * 2 = 6
+	node := &ast.BinaryExpr{
+		Op: token.Star,
+		Left: &ast.BinaryExpr{
+			Op:    token.Plus,
+			Left:  &ast.NumberLit{Value: 1},
+			Right: &ast.NumberLit{Value: 2},
+		},
+		Right: &ast.BinaryExpr{
+			Op:    token.Slash,
+			Left:  &ast.NumberLit{Value: 8},
+			Right: &ast.NumberLit{Value: 4},
+		},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 6 {
+		t.Fatalf("expected 6, got %v", result)
+	}
+}
+
+func TestEvalDeeplyNested(t *testing.T) {
+	// ((2 + 3) * 4) - (10 / 5) = 20 - 2 = 18
+	node := &ast.BinaryExpr{
+		Op: token.Minus,
+		Left: &ast.BinaryExpr{
+			Op: token.Star,
+			Left: &ast.BinaryExpr{
+				Op:    token.Plus,
+				Left:  &ast.NumberLit{Value: 2},
+				Right: &ast.NumberLit{Value: 3},
+			},
+			Right: &ast.NumberLit{Value: 4},
+		},
+		Right: &ast.BinaryExpr{
+			Op:    token.Slash,
+			Left:  &ast.NumberLit{Value: 10},
+			Right: &ast.NumberLit{Value: 5},
+		},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if result != 18 {
+		t.Fatalf("expected 18, got %v", result)
+	}
+}
+
+func TestEvalDivisionByZeroInSubExpr(t *testing.T) {
+	// 1 + (2 / 0) — error should propagate
+	node := &ast.BinaryExpr{
+		Op:   token.Plus,
+		Left: &ast.NumberLit{Value: 1},
+		Right: &ast.BinaryExpr{
+			Op:    token.Slash,
+			Left:  &ast.NumberLit{Value: 2},
+			Right: &ast.NumberLit{Value: 0},
+		},
+	}
+	_, err := Eval(node)
+	if err == nil {
+		t.Fatal("expected division by zero error from sub-expression")
+	}
+}
+
+func TestEvalFloatingPoint(t *testing.T) {
+	// 1.5 + 2.3 = 3.8
+	node := &ast.BinaryExpr{
+		Op:    token.Plus,
+		Left:  &ast.NumberLit{Value: 1.5},
+		Right: &ast.NumberLit{Value: 2.3},
+	}
+	result, err := Eval(node)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if math.Abs(result-3.8) > 1e-12 {
+		t.Fatalf("expected 3.8, got %v", result)
+	}
+}