Step 6: Update REPL to use ParseLine + Evaluator; add function definition REPL tests
diff --git a/repl/repl_test.go b/repl/repl_test.go
index 1c902b1..fe671e7 100644
--- a/repl/repl_test.go
+++ b/repl/repl_test.go
@@ -148,3 +148,104 @@
 		t.Errorf("expected no trailing zeros, got %q", got)
 	}
 }
+
+// --- Function definition tests ---
+
+func TestRun_DefineFunction(t *testing.T) {
+	in := strings.NewReader("f(x) = x + 1\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "defined f") {
+		t.Errorf("expected 'defined f' in output, got %q", got)
+	}
+}
+
+func TestRun_DefineAndCallFunction(t *testing.T) {
+	in := strings.NewReader("f(x) = x + 1\nf(5)\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "defined f") {
+		t.Errorf("expected 'defined f' in output, got %q", got)
+	}
+	if !strings.Contains(got, "6") {
+		t.Errorf("expected output to contain '6', got %q", got)
+	}
+}
+
+func TestRun_DefineMultiParamAndCall(t *testing.T) {
+	in := strings.NewReader("add(x, y) = x + y\nadd(3, 4)\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "defined add") {
+		t.Errorf("expected 'defined add' in output, got %q", got)
+	}
+	if !strings.Contains(got, "7") {
+		t.Errorf("expected output to contain '7', got %q", got)
+	}
+}
+
+func TestRun_RedefineError(t *testing.T) {
+	in := strings.NewReader("f(x) = x\nf(x) = x + 1\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "defined f") {
+		t.Errorf("expected 'defined f' in output, got %q", got)
+	}
+	if !strings.Contains(got, "error:") {
+		t.Errorf("expected 'error:' for redefinition, got %q", got)
+	}
+	if !strings.Contains(got, "already defined") {
+		t.Errorf("expected 'already defined' in error, got %q", got)
+	}
+}
+
+func TestRun_UndefinedFunctionError(t *testing.T) {
+	in := strings.NewReader("f(1)\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "error:") {
+		t.Errorf("expected 'error:' for undefined function, got %q", got)
+	}
+	if !strings.Contains(got, "undefined function") {
+		t.Errorf("expected 'undefined function' in error, got %q", got)
+	}
+}
+
+func TestRun_FuncCallInExpression(t *testing.T) {
+	in := strings.NewReader("f(x) = x * 2\nf(3) + 1\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "7") {
+		t.Errorf("expected output to contain '7', got %q", got)
+	}
+}
+
+func TestRun_CrossFunctionCalls(t *testing.T) {
+	in := strings.NewReader("double(x) = x * 2\nquad(x) = double(double(x))\nquad(3)\n")
+	var out bytes.Buffer
+
+	Run(in, &out)
+
+	got := out.String()
+	if !strings.Contains(got, "12") {
+		t.Errorf("expected output to contain '12', got %q", got)
+	}
+}