Step 4: Add parser tests for Ident, FuncCall, ParseLine, and error cases
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 521455b..99b0e32 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -6,23 +6,14 @@
 	"testing"
 )
 
-// helper: tokenize inline for concise tests
-func tokens(toks ...token.Token) []token.Token {
-	return toks
-}
+// --- Parse (backward compatibility) ---
 
-func tok(typ token.Type, lit string, pos int) token.Token {
-	return token.Token{Type: typ, Literal: lit, Pos: pos}
-}
-
-// --- Success cases ---
-
-func TestParseSingleNumber(t *testing.T) {
-	toks := tokens(
-		tok(token.Number, "42", 0),
-		tok(token.EOF, "", 2),
-	)
-	node, err := Parse(toks)
+func TestParse_SingleNumber(t *testing.T) {
+	tokens := []token.Token{
+		{Type: token.Number, Literal: "42", Pos: 0},
+		{Type: token.EOF, Literal: "", Pos: 2},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
@@ -31,443 +22,433 @@
 		t.Fatalf("expected *ast.NumberLit, got %T", node)
 	}
 	if num.Value != 42 {
-		t.Fatalf("expected 42, got %f", num.Value)
+		t.Fatalf("expected 42, got %v", num.Value)
 	}
 }
 
-func TestParseDecimalNumber(t *testing.T) {
-	toks := tokens(
-		tok(token.Number, "3.14", 0),
-		tok(token.EOF, "", 4),
-	)
-	node, err := Parse(toks)
+func TestParse_BinaryExpr(t *testing.T) {
+	tokens := []token.Token{
+		{Type: token.Number, Literal: "1", Pos: 0},
+		{Type: token.Plus, Literal: "+", Pos: 2},
+		{Type: token.Number, Literal: "2", Pos: 4},
+		{Type: token.EOF, Literal: "", Pos: 5},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	num, ok := node.(*ast.NumberLit)
-	if !ok {
-		t.Fatalf("expected *ast.NumberLit, got %T", node)
-	}
-	if num.Value != 3.14 {
-		t.Fatalf("expected 3.14, got %f", num.Value)
-	}
-}
-
-func TestParseAddition(t *testing.T) {
-	// 1 + 2
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Plus, "+", 2),
-		tok(token.Number, "2", 4),
-		tok(token.EOF, "", 5),
-	)
-	node, err := Parse(toks)
-	if err != nil {
-		t.Fatalf("unexpected error: %v", err)
-	}
-	expr, ok := node.(*ast.BinaryExpr)
+	bin, ok := node.(*ast.BinaryExpr)
 	if !ok {
 		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
 	}
-	if expr.Op != token.Plus {
-		t.Fatalf("expected Plus, got %v", expr.Op)
+	if bin.Op != token.Plus {
+		t.Fatalf("expected Plus, got %v", bin.Op)
 	}
-	assertNumber(t, expr.Left, 1)
-	assertNumber(t, expr.Right, 2)
 }
 
-func TestParseSubtraction(t *testing.T) {
-	// 5 - 3
-	toks := tokens(
-		tok(token.Number, "5", 0),
-		tok(token.Minus, "-", 2),
-		tok(token.Number, "3", 4),
-		tok(token.EOF, "", 5),
-	)
-	node, err := Parse(toks)
+// --- factor: Ident ---
+
+func TestParse_Ident(t *testing.T) {
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "x", Pos: 0},
+		{Type: token.EOF, Literal: "", Pos: 1},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	expr, ok := node.(*ast.BinaryExpr)
+	ident, ok := node.(*ast.Ident)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.Ident, got %T", node)
 	}
-	if expr.Op != token.Minus {
-		t.Fatalf("expected Minus, got %v", expr.Op)
+	if ident.Name != "x" {
+		t.Fatalf("expected 'x', got %q", ident.Name)
 	}
-	assertNumber(t, expr.Left, 5)
-	assertNumber(t, expr.Right, 3)
 }
 
-func TestParseMultiplication(t *testing.T) {
-	// 2 * 3
-	toks := tokens(
-		tok(token.Number, "2", 0),
-		tok(token.Star, "*", 2),
-		tok(token.Number, "3", 4),
-		tok(token.EOF, "", 5),
-	)
-	node, err := Parse(toks)
+func TestParse_IdentInExpr(t *testing.T) {
+	// x + 1
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "x", Pos: 0},
+		{Type: token.Plus, Literal: "+", Pos: 2},
+		{Type: token.Number, Literal: "1", Pos: 4},
+		{Type: token.EOF, Literal: "", Pos: 5},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	expr, ok := node.(*ast.BinaryExpr)
+	bin, ok := node.(*ast.BinaryExpr)
 	if !ok {
 		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
 	}
-	if expr.Op != token.Star {
-		t.Fatalf("expected Star, got %v", expr.Op)
+	left, ok := bin.Left.(*ast.Ident)
+	if !ok {
+		t.Fatalf("expected left to be *ast.Ident, got %T", bin.Left)
 	}
-	assertNumber(t, expr.Left, 2)
-	assertNumber(t, expr.Right, 3)
+	if left.Name != "x" {
+		t.Fatalf("expected 'x', got %q", left.Name)
+	}
 }
 
-func TestParseDivision(t *testing.T) {
-	// 6 / 2
-	toks := tokens(
-		tok(token.Number, "6", 0),
-		tok(token.Slash, "/", 2),
-		tok(token.Number, "2", 4),
-		tok(token.EOF, "", 5),
-	)
-	node, err := Parse(toks)
+// --- factor: FuncCall ---
+
+func TestParse_FuncCallNoArgs(t *testing.T) {
+	// f()
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.RParen, Literal: ")", Pos: 2},
+		{Type: token.EOF, Literal: "", Pos: 3},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	expr, ok := node.(*ast.BinaryExpr)
+	fc, ok := node.(*ast.FuncCall)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.FuncCall, got %T", node)
 	}
-	if expr.Op != token.Slash {
-		t.Fatalf("expected Slash, got %v", expr.Op)
+	if fc.Name != "f" {
+		t.Fatalf("expected 'f', got %q", fc.Name)
 	}
-	assertNumber(t, expr.Left, 6)
-	assertNumber(t, expr.Right, 2)
+	if len(fc.Args) != 0 {
+		t.Fatalf("expected 0 args, got %d", len(fc.Args))
+	}
 }
 
-func TestParsePrecedence(t *testing.T) {
-	// 1 + 2 * 3  →  1 + (2 * 3)
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Plus, "+", 2),
-		tok(token.Number, "2", 4),
-		tok(token.Star, "*", 6),
-		tok(token.Number, "3", 8),
-		tok(token.EOF, "", 9),
-	)
-	node, err := Parse(toks)
+func TestParse_FuncCallOneArg(t *testing.T) {
+	// f(42)
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "42", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 4},
+		{Type: token.EOF, Literal: "", Pos: 5},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	// Root should be Plus
-	expr, ok := node.(*ast.BinaryExpr)
+	fc, ok := node.(*ast.FuncCall)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.FuncCall, got %T", node)
 	}
-	if expr.Op != token.Plus {
-		t.Fatalf("expected Plus at root, got %v", expr.Op)
+	if fc.Name != "f" {
+		t.Fatalf("expected 'f', got %q", fc.Name)
 	}
-	assertNumber(t, expr.Left, 1)
-	// Right should be Star
-	right, ok := expr.Right.(*ast.BinaryExpr)
+	if len(fc.Args) != 1 {
+		t.Fatalf("expected 1 arg, got %d", len(fc.Args))
+	}
+	arg, ok := fc.Args[0].(*ast.NumberLit)
 	if !ok {
-		t.Fatalf("expected right to be *ast.BinaryExpr, got %T", expr.Right)
+		t.Fatalf("expected arg to be *ast.NumberLit, got %T", fc.Args[0])
 	}
-	if right.Op != token.Star {
-		t.Fatalf("expected Star, got %v", right.Op)
+	if arg.Value != 42 {
+		t.Fatalf("expected 42, got %v", arg.Value)
 	}
-	assertNumber(t, right.Left, 2)
-	assertNumber(t, right.Right, 3)
 }
 
-func TestParsePrecedenceMulFirst(t *testing.T) {
-	// 2 * 3 + 1  →  (2 * 3) + 1
-	toks := tokens(
-		tok(token.Number, "2", 0),
-		tok(token.Star, "*", 2),
-		tok(token.Number, "3", 4),
-		tok(token.Plus, "+", 6),
-		tok(token.Number, "1", 8),
-		tok(token.EOF, "", 9),
-	)
-	node, err := Parse(toks)
+func TestParse_FuncCallMultiArgs(t *testing.T) {
+	// f(1, 2, 3)
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "1", Pos: 2},
+		{Type: token.Comma, Literal: ",", Pos: 3},
+		{Type: token.Number, Literal: "2", Pos: 5},
+		{Type: token.Comma, Literal: ",", Pos: 6},
+		{Type: token.Number, Literal: "3", Pos: 8},
+		{Type: token.RParen, Literal: ")", Pos: 9},
+		{Type: token.EOF, Literal: "", Pos: 10},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	expr, ok := node.(*ast.BinaryExpr)
+	fc, ok := node.(*ast.FuncCall)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.FuncCall, got %T", node)
 	}
-	if expr.Op != token.Plus {
-		t.Fatalf("expected Plus at root, got %v", expr.Op)
+	if len(fc.Args) != 3 {
+		t.Fatalf("expected 3 args, got %d", len(fc.Args))
 	}
-	left, ok := expr.Left.(*ast.BinaryExpr)
-	if !ok {
-		t.Fatalf("expected left to be *ast.BinaryExpr, got %T", expr.Left)
-	}
-	if left.Op != token.Star {
-		t.Fatalf("expected Star, got %v", left.Op)
-	}
-	assertNumber(t, left.Left, 2)
-	assertNumber(t, left.Right, 3)
-	assertNumber(t, expr.Right, 1)
 }
 
-func TestParseLeftAssociativity(t *testing.T) {
-	// 1 - 2 - 3  →  (1 - 2) - 3
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Minus, "-", 2),
-		tok(token.Number, "2", 4),
-		tok(token.Minus, "-", 6),
-		tok(token.Number, "3", 8),
-		tok(token.EOF, "", 9),
-	)
-	node, err := Parse(toks)
+func TestParse_FuncCallExprArgs(t *testing.T) {
+	// f(1+2, 3*4)
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "1", Pos: 2},
+		{Type: token.Plus, Literal: "+", Pos: 3},
+		{Type: token.Number, Literal: "2", Pos: 4},
+		{Type: token.Comma, Literal: ",", Pos: 5},
+		{Type: token.Number, Literal: "3", Pos: 7},
+		{Type: token.Star, Literal: "*", Pos: 8},
+		{Type: token.Number, Literal: "4", Pos: 9},
+		{Type: token.RParen, Literal: ")", Pos: 10},
+		{Type: token.EOF, Literal: "", Pos: 11},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	// Root: (1 - 2) - 3
-	expr, ok := node.(*ast.BinaryExpr)
+	fc, ok := node.(*ast.FuncCall)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.FuncCall, got %T", node)
 	}
-	if expr.Op != token.Minus {
-		t.Fatalf("expected Minus at root, got %v", expr.Op)
+	if len(fc.Args) != 2 {
+		t.Fatalf("expected 2 args, got %d", len(fc.Args))
 	}
-	assertNumber(t, expr.Right, 3)
-	left, ok := expr.Left.(*ast.BinaryExpr)
+	// First arg: 1+2
+	_, ok = fc.Args[0].(*ast.BinaryExpr)
 	if !ok {
-		t.Fatalf("expected left to be *ast.BinaryExpr, got %T", expr.Left)
+		t.Fatalf("expected first arg to be *ast.BinaryExpr, got %T", fc.Args[0])
 	}
-	if left.Op != token.Minus {
-		t.Fatalf("expected Minus, got %v", left.Op)
-	}
-	assertNumber(t, left.Left, 1)
-	assertNumber(t, left.Right, 2)
 }
 
-func TestParseParentheses(t *testing.T) {
-	// (1 + 2) * 3
-	toks := tokens(
-		tok(token.LParen, "(", 0),
-		tok(token.Number, "1", 1),
-		tok(token.Plus, "+", 3),
-		tok(token.Number, "2", 5),
-		tok(token.RParen, ")", 6),
-		tok(token.Star, "*", 8),
-		tok(token.Number, "3", 10),
-		tok(token.EOF, "", 11),
-	)
-	node, err := Parse(toks)
+func TestParse_FuncCallInExpr(t *testing.T) {
+	// f(1) + 2
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "1", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 3},
+		{Type: token.Plus, Literal: "+", Pos: 5},
+		{Type: token.Number, Literal: "2", Pos: 7},
+		{Type: token.EOF, Literal: "", Pos: 8},
+	}
+	node, err := Parse(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	expr, ok := node.(*ast.BinaryExpr)
+	bin, ok := node.(*ast.BinaryExpr)
 	if !ok {
 		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
 	}
-	if expr.Op != token.Star {
-		t.Fatalf("expected Star at root, got %v", expr.Op)
-	}
-	assertNumber(t, expr.Right, 3)
-	left, ok := expr.Left.(*ast.BinaryExpr)
+	_, ok = bin.Left.(*ast.FuncCall)
 	if !ok {
-		t.Fatalf("expected left to be *ast.BinaryExpr, got %T", expr.Left)
+		t.Fatalf("expected left to be *ast.FuncCall, got %T", bin.Left)
 	}
-	if left.Op != token.Plus {
-		t.Fatalf("expected Plus, got %v", left.Op)
-	}
-	assertNumber(t, left.Left, 1)
-	assertNumber(t, left.Right, 2)
 }
 
-func TestParseNestedParentheses(t *testing.T) {
-	// ((1 + 2))
-	toks := tokens(
-		tok(token.LParen, "(", 0),
-		tok(token.LParen, "(", 1),
-		tok(token.Number, "1", 2),
-		tok(token.Plus, "+", 4),
-		tok(token.Number, "2", 6),
-		tok(token.RParen, ")", 7),
-		tok(token.RParen, ")", 8),
-		tok(token.EOF, "", 9),
-	)
-	node, err := Parse(toks)
+func TestParse_FuncCallMissingRParen(t *testing.T) {
+	// f(1
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "1", Pos: 2},
+		{Type: token.EOF, Literal: "", Pos: 3},
+	}
+	_, err := Parse(tokens)
+	if err == nil {
+		t.Fatal("expected error for missing closing paren in func call")
+	}
+}
+
+// --- ParseLine: expression statement ---
+
+func TestParseLine_ExprStmt(t *testing.T) {
+	// "1 + 2"
+	tokens := []token.Token{
+		{Type: token.Number, Literal: "1", Pos: 0},
+		{Type: token.Plus, Literal: "+", Pos: 2},
+		{Type: token.Number, Literal: "2", Pos: 4},
+		{Type: token.EOF, Literal: "", Pos: 5},
+	}
+	stmt, err := ParseLine(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	expr, ok := node.(*ast.BinaryExpr)
+	es, ok := stmt.(*ast.ExprStmt)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.ExprStmt, got %T", stmt)
 	}
-	if expr.Op != token.Plus {
-		t.Fatalf("expected Plus, got %v", expr.Op)
+	_, ok = es.Expr.(*ast.BinaryExpr)
+	if !ok {
+		t.Fatalf("expected expr to be *ast.BinaryExpr, got %T", es.Expr)
 	}
-	assertNumber(t, expr.Left, 1)
-	assertNumber(t, expr.Right, 2)
 }
 
-func TestParseComplexExpression(t *testing.T) {
-	// 1 + 2 * 3 - 4 / 2  →  (1 + (2*3)) - (4/2)
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Plus, "+", 2),
-		tok(token.Number, "2", 4),
-		tok(token.Star, "*", 5),
-		tok(token.Number, "3", 6),
-		tok(token.Minus, "-", 8),
-		tok(token.Number, "4", 10),
-		tok(token.Slash, "/", 11),
-		tok(token.Number, "2", 12),
-		tok(token.EOF, "", 13),
-	)
-	node, err := Parse(toks)
+func TestParseLine_ExprStmtFuncCall(t *testing.T) {
+	// "f(1)"
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "1", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 3},
+		{Type: token.EOF, Literal: "", Pos: 4},
+	}
+	stmt, err := ParseLine(tokens)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
-	// Root: (1 + (2*3)) - (4/2)
-	root, ok := node.(*ast.BinaryExpr)
+	es, ok := stmt.(*ast.ExprStmt)
 	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", node)
+		t.Fatalf("expected *ast.ExprStmt, got %T", stmt)
 	}
-	if root.Op != token.Minus {
-		t.Fatalf("expected Minus at root, got %v", root.Op)
-	}
-	// Left: 1 + (2*3)
-	left, ok := root.Left.(*ast.BinaryExpr)
+	_, ok = es.Expr.(*ast.FuncCall)
 	if !ok {
-		t.Fatalf("expected left to be *ast.BinaryExpr, got %T", root.Left)
+		t.Fatalf("expected expr to be *ast.FuncCall, got %T", es.Expr)
 	}
-	if left.Op != token.Plus {
-		t.Fatalf("expected Plus, got %v", left.Op)
-	}
-	assertNumber(t, left.Left, 1)
-	mul, ok := left.Right.(*ast.BinaryExpr)
-	if !ok {
-		t.Fatalf("expected *ast.BinaryExpr, got %T", left.Right)
-	}
-	if mul.Op != token.Star {
-		t.Fatalf("expected Star, got %v", mul.Op)
-	}
-	assertNumber(t, mul.Left, 2)
-	assertNumber(t, mul.Right, 3)
-	// Right: 4/2
-	div, ok := root.Right.(*ast.BinaryExpr)
-	if !ok {
-		t.Fatalf("expected right to be *ast.BinaryExpr, got %T", root.Right)
-	}
-	if div.Op != token.Slash {
-		t.Fatalf("expected Slash, got %v", div.Op)
-	}
-	assertNumber(t, div.Left, 4)
-	assertNumber(t, div.Right, 2)
 }
 
-// --- Error cases ---
+// --- ParseLine: function definition ---
 
-func TestParseEmptyInput(t *testing.T) {
-	toks := tokens(
-		tok(token.EOF, "", 0),
-	)
-	_, err := Parse(toks)
+func TestParseLine_FuncDefSingleParam(t *testing.T) {
+	// "f(x) = x + 1"
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Ident, Literal: "x", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 3},
+		{Type: token.Equals, Literal: "=", Pos: 5},
+		{Type: token.Ident, Literal: "x", Pos: 7},
+		{Type: token.Plus, Literal: "+", Pos: 9},
+		{Type: token.Number, Literal: "1", Pos: 11},
+		{Type: token.EOF, Literal: "", Pos: 12},
+	}
+	stmt, err := ParseLine(tokens)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	fd, ok := stmt.(*ast.FuncDef)
+	if !ok {
+		t.Fatalf("expected *ast.FuncDef, got %T", stmt)
+	}
+	if fd.Name != "f" {
+		t.Fatalf("expected name 'f', got %q", fd.Name)
+	}
+	if len(fd.Params) != 1 || fd.Params[0] != "x" {
+		t.Fatalf("expected params [x], got %v", fd.Params)
+	}
+	// Body should be x + 1
+	bin, ok := fd.Body.(*ast.BinaryExpr)
+	if !ok {
+		t.Fatalf("expected body to be *ast.BinaryExpr, got %T", fd.Body)
+	}
+	if bin.Op != token.Plus {
+		t.Fatalf("expected Plus in body, got %v", bin.Op)
+	}
+}
+
+func TestParseLine_FuncDefMultiParam(t *testing.T) {
+	// "add(x, y) = x + y"
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "add", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 3},
+		{Type: token.Ident, Literal: "x", Pos: 4},
+		{Type: token.Comma, Literal: ",", Pos: 5},
+		{Type: token.Ident, Literal: "y", Pos: 7},
+		{Type: token.RParen, Literal: ")", Pos: 8},
+		{Type: token.Equals, Literal: "=", Pos: 10},
+		{Type: token.Ident, Literal: "x", Pos: 12},
+		{Type: token.Plus, Literal: "+", Pos: 14},
+		{Type: token.Ident, Literal: "y", Pos: 16},
+		{Type: token.EOF, Literal: "", Pos: 17},
+	}
+	stmt, err := ParseLine(tokens)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	fd, ok := stmt.(*ast.FuncDef)
+	if !ok {
+		t.Fatalf("expected *ast.FuncDef, got %T", stmt)
+	}
+	if fd.Name != "add" {
+		t.Fatalf("expected name 'add', got %q", fd.Name)
+	}
+	if len(fd.Params) != 2 || fd.Params[0] != "x" || fd.Params[1] != "y" {
+		t.Fatalf("expected params [x y], got %v", fd.Params)
+	}
+}
+
+func TestParseLine_FuncDefNoParams(t *testing.T) {
+	// "c() = 42"
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "c", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.RParen, Literal: ")", Pos: 2},
+		{Type: token.Equals, Literal: "=", Pos: 4},
+		{Type: token.Number, Literal: "42", Pos: 6},
+		{Type: token.EOF, Literal: "", Pos: 8},
+	}
+	stmt, err := ParseLine(tokens)
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	fd, ok := stmt.(*ast.FuncDef)
+	if !ok {
+		t.Fatalf("expected *ast.FuncDef, got %T", stmt)
+	}
+	if fd.Name != "c" {
+		t.Fatalf("expected name 'c', got %q", fd.Name)
+	}
+	if len(fd.Params) != 0 {
+		t.Fatalf("expected 0 params, got %d", len(fd.Params))
+	}
+}
+
+// --- ParseLine: error cases ---
+
+func TestParseLine_Empty(t *testing.T) {
+	tokens := []token.Token{
+		{Type: token.EOF, Literal: "", Pos: 0},
+	}
+	_, err := ParseLine(tokens)
 	if err == nil {
 		t.Fatal("expected error for empty input")
 	}
 }
 
-func TestParseMissingRParen(t *testing.T) {
-	// (1 + 2
-	toks := tokens(
-		tok(token.LParen, "(", 0),
-		tok(token.Number, "1", 1),
-		tok(token.Plus, "+", 3),
-		tok(token.Number, "2", 5),
-		tok(token.EOF, "", 6),
-	)
-	_, err := Parse(toks)
+func TestParseLine_FuncDefMissingBody(t *testing.T) {
+	// "f(x) ="
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Ident, Literal: "x", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 3},
+		{Type: token.Equals, Literal: "=", Pos: 5},
+		{Type: token.EOF, Literal: "", Pos: 6},
+	}
+	_, err := ParseLine(tokens)
 	if err == nil {
-		t.Fatal("expected error for missing right paren")
+		t.Fatal("expected error for missing function body")
 	}
 }
 
-func TestParseUnexpectedRParen(t *testing.T) {
-	// ) 1
-	toks := tokens(
-		tok(token.RParen, ")", 0),
-		tok(token.Number, "1", 2),
-		tok(token.EOF, "", 3),
-	)
-	_, err := Parse(toks)
+func TestParseLine_FuncDefBadParams(t *testing.T) {
+	// "f(1) = 2" — params must be identifiers
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Number, Literal: "1", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 3},
+		{Type: token.Equals, Literal: "=", Pos: 5},
+		{Type: token.Number, Literal: "2", Pos: 7},
+		{Type: token.EOF, Literal: "", Pos: 8},
+	}
+	_, err := ParseLine(tokens)
 	if err == nil {
-		t.Fatal("expected error for unexpected right paren")
+		t.Fatal("expected error for numeric parameter in func def")
 	}
 }
 
-func TestParseTrailingOperator(t *testing.T) {
-	// 1 +
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Plus, "+", 2),
-		tok(token.EOF, "", 3),
-	)
-	_, err := Parse(toks)
+func TestParseLine_FuncDefTrailingTokens(t *testing.T) {
+	// "f(x) = x 1" — extra token after body
+	tokens := []token.Token{
+		{Type: token.Ident, Literal: "f", Pos: 0},
+		{Type: token.LParen, Literal: "(", Pos: 1},
+		{Type: token.Ident, Literal: "x", Pos: 2},
+		{Type: token.RParen, Literal: ")", Pos: 3},
+		{Type: token.Equals, Literal: "=", Pos: 5},
+		{Type: token.Ident, Literal: "x", Pos: 7},
+		{Type: token.Number, Literal: "1", Pos: 9},
+		{Type: token.EOF, Literal: "", Pos: 10},
+	}
+	_, err := ParseLine(tokens)
 	if err == nil {
-		t.Fatal("expected error for trailing operator")
-	}
-}
-
-func TestParseTrailingTokens(t *testing.T) {
-	// 1 2
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Number, "2", 2),
-		tok(token.EOF, "", 3),
-	)
-	_, err := Parse(toks)
-	if err == nil {
-		t.Fatal("expected error for trailing tokens")
-	}
-}
-
-func TestParseConsecutiveOperators(t *testing.T) {
-	// 1 + * 2
-	toks := tokens(
-		tok(token.Number, "1", 0),
-		tok(token.Plus, "+", 2),
-		tok(token.Star, "*", 4),
-		tok(token.Number, "2", 6),
-		tok(token.EOF, "", 7),
-	)
-	_, err := Parse(toks)
-	if err == nil {
-		t.Fatal("expected error for consecutive operators")
-	}
-}
-
-func TestParseEmptyParens(t *testing.T) {
-	// ()
-	toks := tokens(
-		tok(token.LParen, "(", 0),
-		tok(token.RParen, ")", 1),
-		tok(token.EOF, "", 2),
-	)
-	_, err := Parse(toks)
-	if err == nil {
-		t.Fatal("expected error for empty parentheses")
-	}
-}
-
-// --- Helper ---
-
-func assertNumber(t *testing.T, node ast.Node, expected float64) {
-	t.Helper()
-	num, ok := node.(*ast.NumberLit)
-	if !ok {
-		t.Fatalf("expected *ast.NumberLit, got %T", node)
-	}
-	if num.Value != expected {
-		t.Fatalf("expected %f, got %f", expected, num.Value)
+		t.Fatal("expected error for trailing tokens after function body")
 	}
 }