Step 5: Add Evaluator struct with env, Define, FuncCall support; backward-compat Eval preserved
diff --git a/repl/repl.go b/repl/repl.go
index bc14567..6a86a2e 100644
--- a/repl/repl.go
+++ b/repl/repl.go
@@ -7,6 +7,7 @@
 	"math"
 	"strings"
 
+	"matheval/ast"
 	"matheval/evaluator"
 	"matheval/lexer"
 	"matheval/parser"
@@ -17,6 +18,7 @@
 // Run starts the read-eval-print loop, reading from r and writing to w.
 func Run(r io.Reader, w io.Writer) {
 	scanner := bufio.NewScanner(r)
+	ev := evaluator.New()
 	fmt.Fprint(w, prompt)
 
 	for scanner.Scan() {
@@ -26,32 +28,40 @@
 			continue
 		}
 
-		result, err := evalLine(line)
+		tokens, err := lexer.Tokenize(line)
 		if err != nil {
 			fmt.Fprintf(w, "error: %s\n", err)
-		} else {
-			fmt.Fprintln(w, formatResult(result))
+			fmt.Fprint(w, prompt)
+			continue
+		}
+
+		stmt, err := parser.ParseLine(tokens)
+		if err != nil {
+			fmt.Fprintf(w, "error: %s\n", err)
+			fmt.Fprint(w, prompt)
+			continue
+		}
+
+		switch s := stmt.(type) {
+		case *ast.FuncDef:
+			if err := ev.Define(s); err != nil {
+				fmt.Fprintf(w, "error: %s\n", err)
+			} else {
+				fmt.Fprintf(w, "defined %s\n", s.Name)
+			}
+		case *ast.ExprStmt:
+			result, err := ev.Eval(s.Expr, nil)
+			if err != nil {
+				fmt.Fprintf(w, "error: %s\n", err)
+			} else {
+				fmt.Fprintln(w, formatResult(result))
+			}
 		}
 
 		fmt.Fprint(w, prompt)
 	}
 }
 
-// evalLine tokenizes, parses, and evaluates a single expression string.
-func evalLine(line string) (float64, error) {
-	tokens, err := lexer.Tokenize(line)
-	if err != nil {
-		return 0, err
-	}
-
-	tree, err := parser.Parse(tokens)
-	if err != nil {
-		return 0, err
-	}
-
-	return evaluator.Eval(tree)
-}
-
 // formatResult formats a float64 for display.
 // Whole numbers are printed without decimal points.
 func formatResult(val float64) string {