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")
}
}